O'Reilly logo

Learning iPhone Programming by Alasdair Allan

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Memory Management

The way memory is managed in Objective-C on the iPhone is probably not what you’re used to if you’re coming in from a language such as Java. If you’re writing an application in Objective-C for the Mac, you have the option of enabling garbage collection; however, on the iPhone you are restricted to using reference counting. This isn’t as bad as it seems, and sticking to a few simple rules means that you can manage the memory that is allocated.

Creating Objects

You can create an object in two ways. As shown in the following code, you can manually allocate the memory for the object with alloc and initialize it using init or an appropriate initWith method (e.g., NSString has an initWithString method):

NSString *string = [[NSString alloc] init];
NSString *string = [[NSString alloc] initWithString:@"This is a string"];

Alternatively, you can use a convenience constructor method. For instance, the NSString class has a stringWithString class method that returns an NSString object:

NSString *string =  [NSString stringWithString:@"This is a string"];

In the preceding two cases, you are responsible for releasing the memory you allocated with alloc. If you create an object with alloc, you need to release it later. However, in the second case, the object will be autoreleased. You should never manually release an autoreleased object, as this will cause your application to crash. An autoreleased object will, in most cases, be released at the end of the current function unless it has been explicitly retained.

The Autorelease Pool

The autorelease pool is a convenience that defers sending an explicit release message to an object until “later,” with the responsibility of freeing the memory allocated to objects added to an autorelease pool devolved onto the Cocoa framework. All iPhone applications require a default autorelease pool, and the Xcode template inside the main.m file creates it for us:

int main(int argc, char *argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];1
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];2
    return retVal;
}
1

The default autorelease pool is set up prior to entering the main event loop.

2

The default autorelease pool is drained after exiting the loop.

An additional inner autorelease pool is created at the beginning of each event cycle (i.e., iteration through your application’s event loop), and is released at the end.

The need for and existence of autorelease makes more sense once you appreciate why it was invented, which is to transfer control of the object life cycle from one owning object to another without immediately deallocating the object.

The alloc, retain, copy, and release Cycle

Although the autorelease pool is handy, you should be careful when using it because you unnecessarily extend the time over which the object is instantiated, thereby growing your application’s memory footprint. Sometimes it makes a lot of sense to use autoreleased objects. However, beginning Cocoa programmers often overuse convenience constructors and autoreleased objects.

Note

Apple, writing in its Cocoa Fundamentals guide, officially discourages the use of autorelease objects on the iPhone due to the memory-constrained environment on the device, stating that “Because on iPhone OS an application executes in a more memory-constrained environment, the use of autorelease pools is discouraged in methods or blocks of code (for example, loops) where an application creates many objects. Instead, you should explicitly release objects whenever possible.”

When handling memory management manually using the retain count and the alloc, retain, and release cycle (see Figure 4-1), you should not release objects you do not own. You should always make sure your calls to retain are balanced by your calls to release. You own objects that you have explicitly created using alloc or copy, or that you have added to the retain count of the object using retain. However, you do not own objects you have created using convenience constructors such as stringWithString.

The alloc-retain-release cycle; an object is allocated, retained, and then released twice, bringing the reference count back to zero and freeing the memory

Figure 4-1. The alloc-retain-release cycle; an object is allocated, retained, and then released twice, bringing the reference count back to zero and freeing the memory

When releasing the object, you have the option of sending it either a release message or an autorelease message:

[anObject release];
[anObject autorelease];

Sending a release message will immediately free the memory the object uses if that release takes the object’s retain count to zero, while sending an autorelease message adds the object to the local autorelease pool. The object will be released when the pool is destroyed, normally at the end of the current function.

If your object is a delegate of another object, you need to set the delegate property of that object to nil before you release your original object.

The dealloc Method

The dealloc method is called when an object is released. You should never call this method directly, but instead send a release message to the object, because the object may contain references to other objects that will not be deallocated.

As we did in the HelloWorldViewController class, you should always override the dealloc method in your own objects and in release objects you have created or retained:

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

In this method, we released the label and button instance variables. We then called the dealloc method of the superclass. It is entirely permissible to send a release message to a nil object.

Responding to Memory Warnings

Your code must respond to memory warnings. Let’s look at the HelloWorldViewController implementation from Chapter 3 again. It implements the didReceiveMemoryWarning method:

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

This is where you should release any large blocks of memory—for instance, image or web caches—that you are using. If you ignore a memory warning, your application may crash. The iPhone does not have any sort of virtual memory or swap file; when the device runs out of memory there really is no more memory to allocate. It’s possible, and advisable, to test your application by simulating a memory warning in iPhone Simulator, which you can do by selecting HardwareSimulate Memory.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required