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.
Basics
Simple Examples
Using Accessor Methods
Cases which Often Cause Confusion
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.”
An object may have one or more owners.
By way of an analogy, consider a timeshare apartment.
When an object has no owners, it is destroyed.
To stretch the analogy, consider a timeshare complex that is not loved by the local population. If there are no owners, the complex will be torn down.
To make sure an object you’re interested in is not destroyed, you must become an owner.
You can either build a new apartment, or take a stake in an existing one.
To allow an object in which you’re no longer interested to be destroyed, you relinquish ownership.
You can sell your timeshare apartment.
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”):
Within a given block of code, the number of times you use copy
, alloc
and retain
should equal the number of times you use release
and autorelease
.
You only own objects you created using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc
, newObject
, or mutableCopy
), or if you send it a retain
message.
Implement a dealloc
method to release the instance variables you own.
You should never invoke dealloc
directly (other than when you invoke super’s implementation in a custom dealloc
method).
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.
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); |
} |
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]; |
} |
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]; |
} |
The following sections illustrate common mistakes.
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.
- (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.
- (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).
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.
© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-05-06)