Adventures in the transition from C to Cocoa.

Monday, June 18, 2007

Retain and Release, and Object Creation

Objects in Objective-C maintain a counter to manage how many object point to the object in question. This counter modified by two methods, retain and release, and can be accessed directly by the retainCount method.

Each call to [object retain]; increments this counter, and each call to [object release]; decrements it. The retain/release counter starts at 1. When an object's count drop to 0, it deallocates itself. In Memory Management documentation, you'll find this behavior referred to as Reference Counting.

When an object deallocates itself, it uses its dealloc method. This method is comparable to a destructor in C++. Like C++, dealloc takes no arguments, and has no return value.

To create an object, you've probably noticed the [[object alloc] init]; sequence used. This uses two methods, alloc and init. alloc simply allocates memory. It is essentially the same as malloc() in C. init is used to initialize objects to a usable state. This is the job of a constructor in C++.

Writing our own init and dealloc methods requires a bit of work for things to work right. I'll present an example with both, and then explain the various parts and pieces.

#import <Foundation/Foundation.h>

@interface myObject: NSObject
{
@private
int count;
int refs;
}
-(id)init;
-(void)dealloc;
-(int)getCount;
-(void)incCount;
@end

@implementation myObject
-(id)init
{
self = [super init];
count = 0;
refs = 0;
printf("Running myObject's init method\n");
return self;
}

-(void)dealloc
{
printf("Running myObject's dealloc method\n");
[super dealloc];
}

-(int)getCount
{
return count;
}

-(void)incCount
{
++count;
}
@end

int main()
{
myObject *obj = [[myObject alloc] init];

printf("Object's count: %i\n",[obj getCount]);

[obj incCount];
[obj incCount];
[obj incCount];

printf("Object's count: %i\n",[obj getCount]);

[obj release];

return 0;
}


In this example, we have a simple class called myObject. This object implements a working init and dealloc, and little else.

First, an init method's declaration looks like this: -(id)init;. This means the return type is id, which is certainly not a standard C type. This type is similar to void* in C or C++. This basically means that we'll be returning a pointer to ourselves when the init method returns.

In the implementation of init we have some more magic. The first is self = [super init];. This line has two ideas; self and super. self is basically *this in C++. It's a reference to the object in question. So we're setting our object's reference to [super init]. This, unsurprisingly, uses super, another built-in variable that points to our class's parent class. Setting self to our object's parent's init method is required for proper inheritance.

Following all that stuff, init finishes with return self;. This returns a reference to our object so that the caller (main in this case) can access it.

In the dealloc method, we have a similar expression, [super dealloc];. This method does any necessary clean-up of our object using our class's parent's dealloc method. We are still responsible for any clean up we need to do ourselves (none in this example), but that's it.

Using init, dealloc, retain, and release allow us to start creating real complex classes, and do some basic memory management. There's still much more available in terms of memory management though, in the form of NSAutoReleasePools, but those are reserved for a future entry.

In case you were curious, the above program's output looks like this:
Running myObject's init method
Object's count: 0
Object's count: 3
Running myObject's dealloc method

Categories