iOS Memory Management: Using Autorelease

In a previous post I discussed memory management in iPhone programming. Recall that the iPhone programming environment does not support garbage collection. You must pay attention to objects you create (and therefore own) and must consequently make sure that you properly relinquish ownership. This permits objects to be deleted from memory correctly. You must also recognize when you use objects that you do not own so that you don’t incorrectly release them.

When you develop for the iPhone (or iPad), you use Apple’s Cocoa Touch framework and you write code with Objective-C. Cocoa Touch supports a Model-View-Controller design and maintains (for you) an Autorelease Pool that temporarily holds objects in memory for later release. So, while it’s true that the iPhone environment does not support automatic garbage collection, the environment does provide some automatic memory management assistance. Understanding how the Autorelease Pool works is essential to fully understanding memory management in Objective-C and the Cocoa Touch environment.

iPhone applications run in an event loop, as shown in the diagram below. That is, when your iPhone application launches, the program enters the event loop and waits for a user touch event. When a touch event happens, the Cocoa Touch framework detects the event, creates an event object, and allocates and initializes an autorelease pool. Cocoa Touch then invokes your application event handler, making the event object available.

Event Loop Diagram

iPhone Application Event Loop

Your event handler may put objects in the autorelease pool or, perhaps more commonly, use objects that were put into the autorelease pool by other objects. (For example, the NSString convenience method, stringWithFormat, creates an autorelease object.)

When your event handler finishes execution, control returns to the Cocoa Touch framework. Cocoa Touch drains the autorelease pool, which sends a release message to each object in the pool. Those objects with a retain count of zero are deleted from memory. This means, of course, that just because an object is in an autorelease pool, doesn’t mean that object gets deleted when the pool is drained. Only objects whose retain counts reach zero are deleted.

Cocoa Touch then waits for the next touch event and the cycle begins anew.

So, when should you use autorelease in your code and what do you need to know about autorelease objects?

The most common reason for using autorelease is returning an object you’ve created (and therefore own) to a caller. You can’t release the object inside your method because then you’ll return an invalid object. On the other hand, if you don’t release the object, you’ll create a memory leak (breaking the basic rule that says you must release objects you own). The solution is to put the object in the autorelease pool. Its release is then delayed until the autorelease pool is drained. Let’s look at an example.

Suppose you have a method getLabel in your ViewController class that builds an NSString object for a UILabel component. An event handler calls this method each time the user taps a button. Here is a first attempt.

// Version 1 - Incorrect
- (NSString *)getLabel
{
    NSString * temp;
    if (numberTaps == 1) {
        temp = [[NSString alloc] initWithString:@"time"];
    } else {
        temp = [[NSString alloc] initWithString:@"times"];
    }

    NSString * myLabel = [[NSString alloc] initWithFormat:
			@"You have tapped the button %d %@",
			numberTaps, temp ];
    [temp release];
    // [myLabel release];   ??
    return myLabel;
    // [myLabel release];   ??
}

From our previous post you know that this method is incorrect. Method getLabel owns both temp and myLabel because we create them with alloc. We can release temp (just before the return statement), but what about myLabel? If you release myLabel before the return statement, you return an invalid object. However, if you put the release after the return then the release is never executed.

Our second version uses autorelease with myLabel and release with temp. Apple recommends that you use release instead of autorelease whenever possible to keep the size of the pool small.

// Version 2
- (NSString *)getLabel
{
    NSString * temp;
    if (numberTaps == 1) {
        temp = [[NSString alloc] initWithString:@"time"];
    } else {
        temp = [[NSString alloc] initWithString:@"times"];
    }

    NSString * myLabel = [[NSString alloc] initWithFormat:
			@"You have tapped the button %d %@",
			numberTaps, temp ];
    [temp release];           // delete now
    [myLabel autorelease];    // delete later
    return myLabel;
}

This works fine. However, if you nest the autorelease call with alloc, initWithFormat, and the return statement, the resulting code is cleaner and more readable. Here’s our final version. Note that in this version you must use autorelease with temp instead of release.

// Version 3
- (NSString *)getLabel
{
    NSString * temp;
    if (numberTaps == 1) {
        temp = [[NSString alloc] initWithString:@"time"];
    } else {
        temp = [[NSString alloc] initWithString:@"times"];
    }
    [temp autorelease];        // delete later

    return [[[NSString alloc] initWithFormat:
			@"You have tapped the button %d %@",
			numberTaps, temp ] autorelease];     // delete later
}

Let’s review what happens. The program launches and the Cocoa Touch framework waits for a touch event. The user touches a button (for example), and Cocoa Touch creates both the event and the autorelease pool. Cocoa Touch then invokes your application’s event handler. Inside the event handler, your code invokes method getLabel. Method getLabel creates two NSString objects, one (temp) is used to build the returned NSString and the other is returned to the caller. These NSString objects have a retain count of 1, since method getLabel creates them with alloc.

With method autorelease, these NSString objects go into the autorelease pool, still with a retain count of 1. Cocoa Touch updates the iPhone screen and drains the autorelease pool. Draining the pool sends a release message to our two NSString objects, which makes their retain counts zero. The NSString objects are deleted.

(Note that if you need to create many temporary objects you can create your own autorelease pool inside a program loop or method. This technique drains the local pool more often than the standard event loop pool provided by Cocoa Touch and keeps memory consumption low. In this case, all newly autoreleased objects now go into this new pool. You should always drain a local autorelease pool in the loop or method in which you allocate it.)

What happens when you obtain an object from an NSString convenience method or one of your own methods that use autorelease, but you want to keep the object around for more than one event cycle?

Good question! In this case, the caller needs to claim ownership of the object with method retain. With ownership, you must then make sure that you subsequently release the retained object. We’ll discuss the retain method in the next post.

3 Comments

  1. I read the previous posting plus this posting. I knew the concepts but I didn’t find a good example that would should how they are supposed to be applied. However, I must say that autorelease doesn’t seems to me to be a good idea to used it many times in the code. It should be used it but not being abuse it.

  2. Pingback: Alex Westholm» Blog Archive » Reflections on one month of iOS development

Comments are closed.