Objective-C Lesson 10: Memory Management
Memory on the iPhone is limited. Although each new generation of hardware has increased the amount of physical memory in the device, applications are also taking more memory, and with the introduction of background apps in iOS 4, memory remains a resource to be conserved. iOS automatically quits applications that use too much memory; therefore it’s important to be aware of your memory usage and avoid leaking, which will increase your memory usage, or the equally annoying early release of elements, which will cause your app to crash. These issues are handled by the system in a garbage-collected environment, where the system will automatically release memory as needed, but this adds an overhead and is not available on iOS.
On a similar topic, note that iOS does not have a paging system- data cannot be paged out to disk as needed. You can read and write data to disk, but this is not recommended (or even possible) for any object- only for data files, and even then sparingly, because disk access is inevitably slower than memory access.
Reference Counting
Cocoa’s memory management scheme is based off a reference counting system. It’s a simple concept—every object has a value, known as a reference count. When the object is created, the count is set to 1. Certain actions will increment or decrement this count, and when the count hits zero, the object is immediately freed from memory, at which point you cannot access it again.
Messages
Reference counting principally involves three methods- retain
, release
, and dealloc
. The first two change the reference count; the latter frees up the memory of the objects. You generally override the dealloc
method, as we have been doing in our classes, to free up the memory occupied by the class’s instance variables (usually by calling their dealloc method). Finally, the method always includes a call to super‘s dealloc method, which invariably calls NSObject
‘s dealloc method. It is this specific method that frees up the memory; if you neglect to call this implementation, you get a compiler warning. Also note that you should always override this method, but you should never call it specifically- the system will call that method for you as necessary.
The retain
method increments the retain count by one. It is inherited by all classes, and so can be called on any object, any number of times, at any time. The release method decrements the retain count by one.
Keeping track of the retain count requires some diligence on the part of the programmer. If you under-release an object, the memory will be leaked; if you over-release, you crash.
Memory Management Rules
The rules are simple, really.
-
If the method name has init, copy, or new anywhere in the method name (such as initWithName:, mutableCopy, or newCar), the method implies that the object will have been retained, and it is your responsibility to release that object later This also means that you’ll need to store that object in a variable, to be able to release it again.
NSMutableArray *ma = [someArray mutableCopy]; myMutableArray = ma; [ma release];
-
Any other method name, if the method returns an object, will be autoreleased.
All of Apple’s methods follow these rules, and yours should too.
Memory Management with Properties
When you create a property, you can specify certain parameters that the setters follow. Of note is the retain and copy parameters. Most object properties are declared as retain, which generates a setter like so:
- (void)setText:(NSString *)textValue {
if (![textValue isEqualToString:text]) {
[textValue retain];
[text release];
text = textValue;
}
}
This value is retained, and as such you have to balance it out with a release in the dealloc method. The copy parameter does something similar, except that instead of retaining the new value, it copys it.
One thing to point out, as noted by a comment on the last post, is that in general you do not want to retain delegate objects. Otherwise, you’ll get a recursive memory coupling. For example, a table view’s delegate is generally the view controller which owns the table view itself. Therefore, if the delegate property retained the delegate, the table view would own its delegate, which would own the table view—you couldn’t release any one of them because they own each other. Make sure to avoid this issue.
Autorelease
The autorelease pool is a medium between directly managing memory, and garbage collection. A lot of methods return autoreleased objects- these objects are added to an autorelease pool, which is created in every program. When the drain method is sent to the pool, all of the objects within are sent a release method. This can be useful in a tight loop:
NSAutoreleasePool *tempPool;
for (i = 0; i < n; i++) {
tempPool = [[NSAutoreleasePool alloc] init];
// Create lots of temporary autoreleased objects that take a lot of memory
[tempPool drain];
}
To autorelease an object, simply send the autorelease
method.
Collection Classes
Collection classes, including NSArray
, NSDictionary
, and NSSet
, retain all of the objects that you put into them. When the collection class is deallocated, all of the members in the class are also released.
On any platform, specifically the memory-constrained iOS platform, memory management is an important topic to keep in mind. It requires diligence on the part of the programmer—but the alternative is slow performance and/or crashes, both of which must be avoided. Following the simple rules is the fool-proof way to keep track of your memory management.
This post is part of the Learn Objective-C in 24 Days course.
Previous Lesson | Next Lesson |