< Previous PageNext Page > Hide TOC

Practical Memory Management

This article provides a practical perspective on memory management.

Following a few simple rules can make memory management easy. Failure to adhere to the rules will almost certainly lead at some point to memory leaks, or runtime exceptions due to messages being sent to freed objects.

Contents:

Basics
Simple Examples
Using Accessor Methods
Cases which Often Cause Confusion


Basics

To keep memory consumption as low as possible in an application, you should get rid of objects that are not being used, but you need to make sure that you don’t get rid of an object that is being used. You therefore need a mechanism that allows you to mark an object as still being useful. In many respects, memory management is thus best understood in terms of “object ownership.”

To support this model, Cocoa provides a mechanism called “reference counting” or “retain counting.” Every object has a retain count. An object is created with a retain count of 1. When the retain count drops to 0, an object is deallocated (destroyed). You manipulate the retain count (take and relinquish ownership) using a variety of methods:

alloc

Allocates memory for an object, and returns it with retain count of 1.

You own objects you create using any method that starts with the word alloc or with the word new.

copy

Makes a copy of an object, and returns it with retain count of 1.

If you copy an object, you own the copy. This applies to any method that contains the word copy where “copy” refers to the object being returned.

retain

Increases the retain count of an object by 1.

Takes ownership of an object.

release

Decreases the retain count of an object by 1.

Relinquishes ownership of an object.

autorelease

Decreases the reference count of an object by 1 at some stage in the future.

Relinquishes ownership of an object at some stage in the future.

The rules for memory management are summarized as follows (see also “Memory Management Rules”):

Many classes provide methods of the form +className... that you can use to obtain a new instance of the class. Often referred to as “convenience constructors”, these methods create a new instance of the class, initialize it, and return it for you to use. You do not own objects returned from convenience constructors, or from other accessor methods.

Simple Examples

The following simple examples illustrate the contrast between creating a new object using alloc, using a convenience constructor, and using an accessor method.

The first example creates a new string object using alloc. It must therefore be released.

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

The second example creates a new string object using a convenience constructor. There is no additional work to do.

- (void)printHello {
    NSString *string;
    string = [NSString stringWithFormat:@"Hello"];
    NSLog(string);
}

The third example retrieves a string object using an accessor method. As with the convenience constructor, there is no additional work to do.

- (void)printWindowTitle {
    NSString *string;
    string = [myWindow title];
    NSLog(string);
}

Using Accessor Methods

Sometimes it might seem tedious or pedantic, but if you use accessor methods consistently the chances of having problems with memory management decrease considerably. If you are using retain and release on instance variables throughout your code, you are almost certainly doing the wrong thing.

Consider a Counter object whose count you want to set.

@interface Counter : NSObject {
    NSNumber *count;
}

To get and set the count, you define two accessor methods. In the get accessor, you just pass back a variable so there is no need for retain or release:

- (NSNumber *)count {
    return count;
}

In the set method, if everyone else is playing by the same rules you have to assume the new count may be disposed of at any time so you have to take ownership of the object—by sending it a retain message—to ensure it won’t be. You must also relinquish ownership of the old count object here by sending it a release message. (Sending a message to nil is allowed in Objective-C, so this will still work if count hasn't yet been set.) You must send this after [newCount retain] in case the two are the same object—you don't want to inadvertently cause it to be deallocated.

- (void)setCount:(NSNumber *)newCount {
    [newCount retain];
    [count release];
    // make the new assignment
    count = newCount;
}

These examples present a simple perspective on accessor methods. They are described in greater detail in “Accessor Methods.”

Since the Counter class has an object instance variable, you must also implement a dealloc method. It should relinquish ownership of any instance variables by sending them a release message, and ultimately it should invoke super’s implementation:

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

Implementing a reset method

Suppose you want to implement a method to reset the counter. You have a couple of choices. The first uses a convenience constructor to create a new NSNumber object—there is therefore no need for any retain or release messages. Note that both use the class’s set accessor method.

- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
}

The second creates the NSNumber instance with alloc, so you balance that with a release.

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero];
    [zero release];
}

Common Mistakes

The following sections illustrate common mistakes.

Accessor not used

The following will almost certainly work correctly for simple cases, but tempting as it may be to eschew accessor methods, this will also almost certainly lead to a mistake at some stage.

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [count release];
    count = zero;
}

Note also that if you are using key-value observing (see Key-Value Observing Programming Guide), then changing the variable in this way is not KVO-compliant.

Instance leaks

- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero];
}

The retain count of the new number is 1 (from alloc) and is not balanced by a release within the scope of the method. The new number is unlikely ever to be freed, which will result in a memory leak.

Instance you don’t own is sent release

- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
    [zero release];
}

Absent of any other invocations of retain, this will fail the next time you access count after the current autorelease pool has been released. The convenience constructor method returns an autoreleased object, so you don't have to send another release. Doing so will mean that when the release due to autorelease is sent, it will reduce the retain count to 0, and the object will be freed. When you next access count you will be sending a message to a freed object (typically you'll get a SIGBUS 10 error).

Cases which Often Cause Confusion

When you add an object to a collection such as an array, dictionary, or set, the collection takes ownership of it. The collection will relinquish ownership when the object is removed from the collection or when the collection is itself released. Thus, for example, if you want to create an array of numbers you might do either of the following:

NSMutableArray *array;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
    NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];
    [array addObject:convenienceNumber];
}

In this case, you didn’t invoke alloc, so there’s no need to call release. There is no need to retain the new numbers (convenienceNumber), since the array will do so.

NSMutableArray *array;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
    NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i];
    [array addObject:allocedNumber];
    [allocedNumber release];
}

In this case you do need to send allocedNumber a release message within the scope of the for loop to balance the alloc. Since the array retained the number when it was added by addObject:, it will not be deallocated while it’s in the array.

To understand this, put yourself in the position of the person who implemented the collection class. You want to make sure that no objects you’re given to look after disappear out from under you, so you send them a retain message as they’re passed in. If they’re removed, you have to send a balancing release message, and any remaining objects should be sent a release message during your own dealloc method.



< Previous PageNext Page > Hide TOC


© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-05-06)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.