paulgorman.org

Objective-C & Cocoa

Overview

Programming on Mac OS X is done in the Objective-C language. Objective-C is a superset of C. Cocoa is a collection of frameworks, notably Foundation, AppKit, and Core Data. Frameworks are like libraries. Frameworks can have sub-frameworks. Apple supplies free developer tools, and their IDE is called Xcode. Interface Builder is a GUI creation tool tightly integrated with Xcode.

If you haven't done any C stuff in a while, you might want to looks at the C refresher at the end of this document. There is also a refresher on object-oriented terms and concepts.

Objective-C

Objective-C hello, world!


/*****************************************************************************
    Interface file HelloWorld.h declares class HelloWorld:
*****************************************************************************/

#import <Foundation/Foundation.h>
// import is like include, but prevents accidentally including the same
// file multiple times. Just use import.

@interface HelloWorld : NSObject
{
    NSString *message;
}
// "-" indicates an instance method, "+" a class method.
- (void)setMessage:(NSString *)aMessage;
- (NSString *)message;
- (void)logMessage:(NSString *)aMessage;
@end

/*****************************************************************************
    Implementation file HelloWorld.m defines class HelloWorld:
*****************************************************************************/

#import "HelloWorld.h"

@implementation HelloWorld
- (void)setMessage:(NSString *)aMessage
{
    [message autorelease];
    message = [aMessage retain];
}
- (NSString *)message
{
    return message;
}
- (void)logMessage:(NSString *)aMessage
{
    NSLog(aMessage);
}
@end

/*****************************************************************************
    File main.m:
*****************************************************************************/
#import <Foundation/Foundation.h>
#import "HelloWorld.h"

int main(int argc, const char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    HelloWorld *myWorld = [[HelloWorld alloc] init];
    [myWorld setMessage:@"Hello, world!"];
    [myWorld logMessage:[myWorld message]];
    [myWorld release];
    [pool release];
    return 0;
}

Compiling hello, world!

In OS X, from the command line, the above three files can be compiled with:

gcc -framework Foundation main.m HelloWorld.m -o hello

On my Linux box with GNUstep installed, I had to first run:

. /usr/share/GNUstep/Makefiles/GNUstep.sh

I also had to use make to compile, with this GNUmakefile:

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = hello
hello_HEADERS = HelloWorld.h
hello_OBJC_FILES = HelloWorld.m main.m

include $(GNUSTEP_MAKEFILES)/tool.make

With the above done, running make in the directory which contains the source files and the makefile will create an obj directory. That directory will contain a compiled binary named hello.

Sending Objective-C messages

[receiver message];  // Called "sending a message" instead "calling a method"
[myRectangle display];
[myRectangle setWidth:20.0]; // Message with an argument
[myRectange setOriginX:5.0 y:10.0]; //Message with multiple arguments
// Nested message sets color of one rectangle to color of another rectangle:
[myRectangle setPrimaryColor:[otherRect primaryColor]];

Memory management by reference counting

Reference counting is the way to manually manage memory in Objective-C. When an object is created with alloc it gets one reference count. Everytime that object is sent a retain message, its reference count increases by one. Everytime the object gets a release message, its reference count decreases by one. When an objects reference count reaches zero, its memory is freed.

OS X applications have the option to use automatic garbage collection, but many programs and programmers do not use it. Also, garbage collection is not supported by the iPhone SDK.

The Vehicle class example

/*****************************************************************************
    Interface file Vehicle.h:
*****************************************************************************/

#import <Cocoa/Cocoa.h>  // import directive prevents multiple includes

@interface Vehicle : NSObject  // Vehicle inherits from parent class NSObject
{
    NSString *color;  // Declare instance variables
    float width;
    float length;
}
- (NSString *)color;  // Accessors. - is instance method, + is class method
- (void)setColor:(NSString *)aColor; 
- (float)width;
- (float)length;
- (void)setWidth:(float)aWidth length:(float)aHeight;
@end  // Ends class declaration


/*****************************************************************************
    Implementation in file Vehicle.m:
*****************************************************************************/

#import "Vehicle.h"

@implementation Vehicle
- (id)init
{
    if (self = [super init]) {
        [self setColor:@"black"];
        [self setWidth:6.5 length:10];
    }
    return self;
}
- (NSString *)color
{
    return color;
}
- (void)setColor:(NSString *)aColor
{
    [color autorelease]; // -autorelease is safer than -release here.
    color = [aColor retain];
}
- (float)width
{
    return width;
}
- (void)setWidth:(float)aWidth (float)aLength
{
    [width autorelease];
    [length autorelease];
    width = [aWidth retain];
    length = [aLength retain];
}
- (void)dealloc
{
    [color release];
    [width release];
    [length release];
    [super dealloc];
}
@end

Properties

Properties can be used to automate the creation of basic accessor methods. The above Vehicle class interface and implementation can be written like this:


/*****************************************************************************
    The interface file Vehicle.h:
*****************************************************************************/

#import <Cocoa/Cocoa.h>

@interface Vehicle : NSObject
{
    NSString *color;
    float width;
    float length;
}
@property (retain) NSString *color;
@property (retain) float width;
@property (retain) float length;
@end


/*****************************************************************************
    The implementation file Vehicle.m:
*****************************************************************************/

#import "Vehicle.h"

@implementation Vehicle
@synthesize color, width, length;
@end

Protocols and delegates

Protocols define methods that any class can implement.

@protocol MyProtocol
- (void)myMethod;
// Protocols do not have instance methods
@end

A class that implements protocols can declare so in its interface declaration:

@interface MyClass : NSObject < MyProtocol, AnotherProtocol > {

Protocols can be used for the delegate design pattern. For example, if you have various classes that want to receive notifications about the state of the application, your classes can implement the UIApplicationDelegate protocol. The UIApplication object can then deliver notification to its delegate object, and all your objects that implement the protocol can get those notifications from the delegate.

Cocoa

Additional material

C refresher

/*****************************************************************************
    C comments
*****************************************************************************/

// This is a single line comment.
/* This...
...is a...
...multi-line comment. */

/*****************************************************************************
    C including files
*****************************************************************************/

# include <stdio.h> // Include C standard libraries
# include nifty.h // Include other library

/*****************************************************************************
    C variables
*****************************************************************************/

const float pi = 3.131592 // Declare & define a constant
int x; // Declare variable
x = 6; // Define variable
char a, b, myArray[40]; // More declarations
int myNumbers = { 2, 5, 89, 3 }; // Delare & define an array
myNumbers[2] = 57; // Re-define one element of an array
int myMultiArray[3][5] // three rows, five columns
myMultiArray[0][3] = 74 // first row, forth column get value '74'
// C doesn't have a basic string type, only char arrays:
char myString[] = "Hello, world!";
unsigned long int x = 68593938585930;
float y = 2.725;
int z = (int)y; // Cast the float y as an int
typedef enum { monday, tuesday, wednesday, thursday, friday } Weekday;
Weekday myDay = thursday; // Never could get the hang of Thursdays
typedef struct {
    char *model;
    int doors;
    int wheels;
} Vehicle;
Vehicle myTrike;
myTrike.model = "Trek";
myTrike.doors = 0;
myTrike.wheels = 3;

/*****************************************************************************
    C loops and conditionals
*****************************************************************************/

int n = 0;
while ( n < 10 ) {
    n = n++;
}

int n = 10;
do {
    n -= n;
} while ( n != 0 );

for ( int i = 0; i <= 100; i++ ) {
    printf( "The variable i is %i.\n", i );
}

if ( n == 1 ) {
    printf( "n is 1.\n" );
} else if ( n == 2 ) {
    printf( "n is 2.\n" );
} else {
    printf( "n is not 1 or 2!\n" );
}

/*****************************************************************************
    C functions
*****************************************************************************/

#include <stdio.h>

int subtract( int x, int y) {
    return x - y;
}

int main() { // Execution starts here...
    int foo = 10;
    int bar = 6;
    int myNumber = subtract( foo, bar ); // myNumber gets the value 4.
    printf( "My number is %i.\n", myNumber );
    return 0;
}

// Note to self re gcc compiler arguments:
// gcc -std=c99 -lgmp foo.c -o foo

/*****************************************************************************
    C variable scope
*****************************************************************************/

#include <stdio.h>

int foo = 0 // Global across program
static int bar = 9 // Global, but only in this file

void myFunction() {
    int foo = 3 // Local; does not conflict with global foo
    static int nerf = 22 // Local, but persists even after function returns
}

/*****************************************************************************
    C operators
*****************************************************************************/

int x = 0; // assignment
x = 1 + 8 - 2 * 3 / 5; // arithmetic add, subtract, multiply, divide
a % b; // Modulo: the remainder of a/b; 9 % 3 == 0; 7 % 3 == 1
x += 99; x -= 45; // Same as x = x + 99, x = x - 45; also *= and /=
i++; ++i; i--; --i; // increment, postfix and prefix; decrement
if ( x == y ) thingy(); // test equality
if ( x != y ) whatsits(); // test non-equality
if ( x > y ) blerg(); // greater than?
if ( x < y ) blerg(); // less than?
if ( x > 2 && y = 1 ) nerf(); // logical and
if ( x == 2 || x == 97 ) hmph(); // logical or
if ( x >= 1 || x <= 23 ) hmph(); // greater or equal? less or equal?
if ( !( x == y ) ) woah(); // negation

/*****************************************************************************
    C pointers
*****************************************************************************/

int number = 3;
/* Create a pointer of type pointer to int, and point it towards the 
   value stored in number: */
int *pointerToNumber = &number; // & is the address-of operator.
printf( "Number: %i\n.", *pointerToNumber ); // * is the dereference operator.

/*****************************************************************************
    C memory
*****************************************************************************/

int *scores;
scores = malloc( sizeof( int ) * 10 ); // Allocate memory for ten integers.
*scores = 22;
scores++; // Move pointer forward to next memory address.
*scores = 127;
free( --scores ); // Pointer needs to be back at original position to free.

The C stack and the heap

C stores local variables in a reserved block of memory called the stack. The stack is a FILO (first in, last out) data structure. It works like a stack of dinner dishes: you first put down one freshly washed dish, and stack others on top of it as you finish washing them; as you need to use dishes to eat, you take them off the top of the stack. You never take plates off the bottom or from the middle of the stack.

Every time you call a function or start a code block with a curly brace, a chunk of memory is reserved on the top of the stack. Chunks of memory reserved by previous function calls are "pushed down" beneath it. When you code block ends or your function returns, that memory chunk is freed and its data goes out of scope.

Trying to cram too large a value onto the stack or creating an infinite recursion will cause a stack overflow, crashing your program. Mostly, though, the compiler handles the allocation and freeing of stack memory for you.

The OS assigns one stack for each thread. Single-threaded programs only have one stack.

The heap is another reserved block of memory, one much larger than the stack. You can access any part of memory in the heap, not just the chunk on top. To use heap memory, allocate it with malloc(). Memory allocated on the heap, unlike memory on the stack, will stick-around beyond one little block of code. In fact, heap memory will pile up like dirty laundry unless you explicitly call free() to notify the heap allocator that it can reuse that memory.

If you allocate heap memory but never free it, you've created a memory leak. That's bad. If you assign data too large for the amount of memory you reserved, that's a heap overflow, and it's bad too.

Regardless of the number of threads, they all share one heap. Memory allocation on the heap is slower than memory allocation on the stack.

More about C pointers

Pointers do a bunch of different things in C. They make arrays and structs possible, and also let functions handle pass-by-address arguments.

Also, since C functions can only return a single value, it's a common idiom to pass pointers to multiple or complex variables into a function, and fill them by reference, rather than returning them as values.

#include <stdio.h>

int squareAndCube(int x, int *squared, int *cubed) {
    *squared = x * x;
    *cubed = x * x * x;
    return 1;
}

void main() {
    int a, b, c;
    a = 2;
    squareAndCube(a, &b, &c);
    printf("%d squared is %d; %d cubed is %d.\n", a, b, a, c);
    // Prints "2 squared is 4; 2 cubed is 8."
}

With "normal" variables, the declaration int x; causes the compiler to reserve 4 bytes of memory (or however big integers are on your local system). The compiler also adds x and the address of x to the symbol table. The assignment x = 2; places (at runtime) the value in the reserved memory block. So, there are two values associated with x: its memory address (sometimes called the lvalue) and the value '2' (sometimes called the rvalue).

int x, y;
x = 2;
y = 5; // y here means y's address or lvalue (left side of =)
x = y; // y here means y's stored value or rvalue (right side of =)

A pointer is a different kind of variable that holds an lvalue (a memory address) instead of an rvalue.

The declaration int *z; tells the compiler to reserve enough bytes to store a memory address. The type specified (e.g.—int) is the type of the variable to which the pointer will point. The pointer itself is not of that type; the pointer's type is pointer.

The assignment z = &x; gives the pointer z the address of x. The & operator forces the compiler to get the lvalue of x (its address), even though x appears on the right side of the equal sign here. z points to x.

The * dereference/indirection operator tells the compiler the use the rvalue. So, *z = 9 assigns 9 to the rvalue at which z points (z points at x, so x is now set to 9).

int myInteger, *myPointer;

myPointer = 42; // WRONG! Nonsense. A pointer type can't hold an int.
myInteger = 42; // Normal variable. Fine.
myPointer = &myInteger; // Cool. myPointer holds the address of myInteger.
*myPointer = 21; // Cool. myInteger is now 21.

Object-oriented terms and concepts

Classes are blueprints for objects. Objects have both attributes (like variables), and methods (like functions). In objected-oriented languages, one sends messages to objects (whereas one calls functions in procedural languages). Child Subclasses can be derived from parent superclasses; subclasses inherit attributes and methods from their superclasses. In some languages, multiple inheritance allows a subclass to inherit attributes and methods from multiple parents, but in Objective-C a subclass only inherits from one superclass (but see protocols and delegates below).

Encapsulation means other objects that send a bark() message to a Dog object don't need to know about the implementation details of bark() (and therefore those other, non-Dog objects don't need updating if the hidden implementation details of bark() change). Encapsulation lets classes hide both the implementation of their methods and also hide their data. Accessors are methods that set or get the value of an encapsulated attribute. Encapsulation is a form of abstraction.

Polymorphism means that both the Dog class and the Fox class can have bark() methods which do slightly different things, but they don't conflict with each other. The term polymorphism is also sometimes used to mean overloading. Overloading lets a method do different things depending upon the type of data passed to it. In Python, for example, the "+" operator does arithmetical addition for numbers (2 + 2 == 4) and concatenation for strings ("foo" + "bar" == "foobar"). Polymorphism is another form of abstraction.

Protocols and delegates provide some of the benefits of multiple inheritance. Delegation is a design pattern in which Object A creates a delegate Object B, and Object B implements a method of Object C, where Object C is not a parent of Object A. Protocols declare methods that can be implemented by any class; classes which are similar but not hierarchically related might implement the same protocol.

Objective-C also has dynamic typing, dynamic binding, and dynamic loading. Dynamic typing is waiting until runtime to determine the class of an object. Dynamic binding is waiting till runtime to determine which method to invoke. Dynamic loading allows new component to be added to a running program.

Read Apple's Object-Oriented Programming with Objective-C for more information.

Recommended Books