Objective-C Memory Management

Every object that is derived from NSObject has the ability to be managed by retain counting. When an object is created (alloc) the system will allocate memory for the object and set a retain count to 1. The count can be incremented when a new instance of this object exist (assignment for example) and decrement when an object instance is released. Once the retain count is going to zero the object will be destructed and the memory gets freed. This post is about that retain count.

Basis functions

There some basic functions an object has automatically when derived from NSObject
alloc
allocates the necessary memory for a new object and returns it with a reference count of one.
release
Decreases the receiver’s reference count by one.
autorelease
Adds the receiver to the application’s autorelease pool, which will decrease the receiver’s reference count by “1″ (at some point in the future) . Autorelease is much slower than release, so use it wisely.
retain
Increases the receiver’s reference count by one.
copy
Makes a copy of an object, and returns it with retain count of one.

Example 1

Objects you allocate with alloc should be released with release and then grounded to nil.

- (void)myMethod
{
    MyClass * myObject;
    myObject= [MyClass alloc];
 
    // Do stuff
 
    [myObject release];
    myObject = nil;
}

If you don’t want to release your objects manually you could make the use of an autorelease pool. If you send theautorelease message to an object it will be sent and added to the autorelease pool. At the end of an event loop, this autorelease pools is released by the application object and then all of the objects contained within it are released, too. At the beginning of the next loop, a new pool is created and so on.

Example 2

An autoreleased Object is considered to be valid whithin the scope where it received the autorelease message.

- (void)myMethod
{
    MyClass * myObject;
    myObject = [MyClass alloc];
    [myObject autorelease];
 
    // myObject will be valid until
    // the very end of this method!
}

Example 3

When you have a method that creates and returns an object autorelease is quite useful to make sure that an object will be released on time.

- (NSString *)myFunction
{ 
    NSString *myString = [[NSString alloc] initWithString:@"Hello String."];
    [myString autorelease];  // we have to do this to prevent a memory leak
    return myString;
}

Example 4

There are so named convencience constructors which will return autoreleased objects. If you use them and you want to keep them at the end of a method block, you have to sent a retain message. So if you keept in mind to call release or autorelease for each alloc, you know have to complete the rule to : for each alloc and for each retain you have to call release or autorelease.

// if we assume that myVar exists
myVar = [[NSString stringWithCString:"Hello String."] retain];

So we can breakdown the memory management to some rules :

Rule 1 : Retention Count

  1. Within a given block, the use of –copy, –alloc and –retain should be equal the use of –release and –autorelease.
  2. Objects created using convenience cunstructors (e.g. NSString’s strintWithCString) are concideredautoreleased.
  3. Implement a –dealloc method to release the instance variables you own.

Example 1: alloc and release

- (void)printHello
{
	NSString *string;
	string = [[NSString alloc] initWithString:@"Hello"];
	NSLog(string);
	// we created string with alloc -- release it
	[string release];
}

Example 2: Convenience constructor

- (void) printHello
{
	NSString *string;
	string = [NSString stringWithFormat:@"Hello"];
	NSLog(string);
	// we created string with convenience constructor
	// we can assume it's autoreleased
}

Rule 2 : Always use accessor methods

If you are using retain and release on a classes instance variables throughout your code, you are almost certainly doing the wrong thing. Use conistently accessor methods of classes to decrease memory management problems.

Example

@interface Counter : NSObject
{
	NSNumber *count;
}
 
- (NSNumber *)count
{
	return count;
}
 
- (void)setCount:(NSNumber *)newCount
{
	[newCount retain];
	[count release];
	count = newCount;
}
 
- (void)dealloc
{
	[self setCount:nil];
	[super dealloc];
}
- (void)reset
{
//*/
//    YOU COULD DO :
	NSNumber *zero = [NSNumber numberWithInt:0];
	[self setCount:zero];
/*/
//    OR (if you have to alloc here) :
	NSNumber *zero = [[NSNumber alloc] initWithInt:0];
	[self setCount:zero];
	[zero release];
//*/
}

Rule 3 : Ownership in collections like NSArray, NSDictionary, NSSet etc.

When you add an object to a collection class the collection retains it. A release message will be sent when the collection it self gets released. To understand this, think of what would you do if you are planning a collection class. You wanted to make sure that no objects you were given to look after disappeared out from under you else where, so you send them a -retain message as they’re passed in. If they’re removed, you have to send a balancing -release message. Same when your collection will be released, any remaining objects in your collection should be sent a -release message during your own -dealloc method.

The following examples are both correct.

Example 1

-(void) myMethod
{
    NSMutableArray *array;
    int i;
    // ...
    for (i = 0; i < 10; ++i)
    {
        NSNumber *n = [NSNumber numberWithInt: i];
        [array addObject: n];
    }
}

Example 2

-(void) myMethod
{
    NSMutableArray *array;
    int i;
    // ...
    for (i = 0; i < 10; ++i)
    {
        NSNumber *n = [[NSNumber alloc] initWithInt: i];
        [array addObject: n];
        [n release];
    }
}

Release and dealloc

The release message will be sent by you or by an autorelease pool. The release method of an object decreases the reference counter by one, and if its zero calls the dealloc method.

Vicious retain circles

Guess we have an object A is retaining object B and object B is retaining object A. Now these objects never reach a zero retain count. Just in that moment the reference count gets to one, they are effectively out of the program. This is called vicious retain circles and it is possible to have very complex retain cirules like that where the minumum count is even higher than one. To avoid this you should always plan your class dependency wisely. Lets take a look on the Cocoa view hierachy. A NSView can have several subviews which have subviews and so on. On the other side a NSView has also a pointer to the superview. Why doesn’t this ends in an vicious retain circle?  Well, NSViews retains their subviews but not their superview.

Further Links

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.