Cocoa memory management 101

Ok, so here it comes, my first Cocoa posting. I am pretty new to Cocoa and therefore this might be something you are more than familiar with, but for me it is handy to have a quick reference written down where I can look up things. So today I would like to talk about memory management. My coding background lies about 70% in Java, 20% in PHP and 10% in C/C++. So in the past mostly I did not have to worry about memory management topics, although I am familiar with the low level C/C++ way of allocating and releasing memory. Cocoa uses an approach somehow in the middle between automatic garbage collection and manual management. Once you get used to it, it is a very powerful tool and you have a much better control over the memory consumption in your applications than in Java while being less error prone than the C/C++ mechanism.

If you want to dive into the details of Cocoa memory management, I recommend you read the Memory management Programming Guide For Cocoa from Apple. For me it was the main source of information about this topic.

The retain cycle

Cocoa objects use an Attribute called retainCount which keeps track how many other objects claim ownership to it. Whenever you want to keep a reference to an object you send it the retain message, which will increase its retainCount. When you don’t need the reference any more you send a release message and the count will decrease. Once the retainCount drops to 0, the object will be disposed and the memory freed.

The most important rule you have to remember is:

Whenever you create an object via methods that contain alloc, new and copy in their name, or you increase the retainCount via retain you need to release that object in the same context.

For example let’s take a look at the following method:

+ (void)doSomething
{
    id *myObject = [[SomeObject alloc] init];
    
    // do something with the object
   
    [myObject release];
}

We are creating a method variable pointing to a freshly created object. The variable will be disposed when we exit the method, therefore we need to make sure to release the object it is pointing to before exiting the method.

The same principal applies to instance variables of a class. Here you need to make sure that you release all the instance variables this class might have created, before disposing the class object. This is usually done in the dealloc method of the class.

When working with instance variables you should follow the best practice to access the variables only through their accessor methods. That way you can ensure a transparent handling of the release and retain operations. Let’s take a look at a typical setter method:

- (void)setName:(NSString *)aName
{
    aName = [aName copy];
    [name release];
    name = aName;
}

So the setter first creates a new copy of the object, then releases the old object and last it will store the reference to the new object in the instance variable. The order of the operations is especially importing. Because if we would first release the old object, and the new object would be a pointer to the old object, we would loose our data.

Now that the setter always allocates a new object, we need to make sure to release it when the class is destroyed:

- (void)dealloc
{
    [name release];
    [super dealloc];
}

We don’t have to worry about the name object being nil, because Objective-C can send messages to nil-objects without generating null-Pointer exceptions or crashing.

Autorelease objects

So naturally the next question is: How do we return objects we have created inside a method when we have to release them before we exit the method?

This is the domain of the autorelease objects. Instead of sending an object the release message, you could send it the autorelease message, which will mark that object for later deletion. So, the retainCount will be reduced, but the object will not be disposed and we can use it in the code that called our method.

So how does this work? Basically it adds the object to a so called autorelease pool. This pool is initialized automatically every time an event cycle is triggered (for example via a MouseDown event). During the event cycle, all autorelease objects will be added to that pool and they will be disposed once the event cycle has finished. This means that you don’t have to worry about retaining an autorelease object as long as you use it only during a single user action. You only need to retain it if you want to store it between user actions.

If you stick to the convention to access your instance variables via the getter and setter methods, you do not need to worry about working with autoreleased objects, because the setter methods would automatically retain the object. That’s a best practice which really helps keep your code clear.

Besides the automatic autorelease pools, you can also create your own pool by creating a new instance of the object NSAutoreleasePool. The creation of your own autoreleass pool is mandatory in the following cases:

  • You are writing a command line tool
  • You are spawning a new thread

There is another use for custom pools. When you are working with a lot of autorelease objects inside a loop statement, this will lead to all those objects being stored in the pool until the event loop finishes. It is much more efficient to release the memory at the end of each loop cycle. In that case we would create a autorelease pool inside the loop:

for (i = 0; i < 1000; i++) {
    NSAutoReleasePool *myPool = [[NSAutoReleasePool alloc] init];

    NSString *someVariable = [NSString stringWithString:@"Some text"];

    // do some more stuff with lots of autorelease objects

    [myPool release];
}

In the above example, all the autorelease objects which have been created inside the loop, will be disposed of as soon as we release the pool object. This is a very powerful concept to keep your memory consumption low.

Note on collections

Collection objects retain the objects which are stored in the collection. You can release them once you added them to the collection, but keep in mind, that once the collection object is released, it will also release all objects stored in that collection.

Leave a Reply