
Understanding the autorelease pool mechanism
When you first start developing for Cocoa (iOS or Mac OS) you quickly learn to follow the standard alloc
, init
, and (eventually) release
cycles:
// Allocate and init NSMutableDictionary *dictionary = [[NSDictionary alloc] init]; // Do something with dictionary // ... // Release [dictionary release];
This is great until you discover the convenience of just doing the following:
// Allocate and init NSMutableDictionary *dictionary = [NSDictionary dictionary]; // Do something with dictionary // …
Let's look inside and see what actually happens:
NSMutableDictionary *dictionary = [[NSDictionary alloc] init]; return [dictionary autorelease];
This approach is called autorelease pools and they are a part of the Automated Reference Counting (ARC) memory management model used by the Cocoa platform.
The ARC compiler will autorelease any object for you, unless it's returned from a method that starts with new
, alloc
, init
, copy
, or mutableCopy
in its name. As before, these objects are placed into an autorelease pool, but in order to introduce a new language construct, NSAutoreleasePool
has been replaced by @autoreleasepool
, a compiler directive. Even using ARC, we are still free to use autorelease
messages to drain/create our pool at any time. It doesn't affect the compiler when implementing retain
and release
messages, but provides hints when it's safe to make autoreleased objects go out of scope.
Cocoa frameworks (Foundation Kit, Application Kit, and Core Data) have some suitable methods to handle basic classes that inherit from NSObject
, as NSString
, NSArray
, NSDictionary
, and many more. These methods quickly allocate, initialize, and return the created object for you, which will also be autoreleased without you worrying about it so much.
Basically, an autorelease pool stores objects and when it's drained, it just sends the object a release
message. The NSAutoreleasePool
class is used to support Cocoa's reference-counted memory management system.
Autorelease pools were made by Apple and have been part of the language itself since OS X 10.7. If a program references the NSAutoreleasePool
class while in ARC mode, it's considered invalid and is rejected in the build phase. Instead, in ARC mode, you need to replace it with @autoreleasepool
blocks, thus defining a region where an autorelease pool is valid, as you can see in the following code:
// Code in non-ARC mode NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init]; // Taking advantage of a local autorelease pool. [myPool release];
In ARC mode, however, you should write:
@autoreleasepool { // Taking advantage of a local autorelease pool. }
Even if you don't use ARC, you can take advantage of @autoreleasepool
blocks that are far more effective than the NSAutoreleasePool
class.
Opposite to an environment that uses garbage collection, in one with reference counting, every object that receives an autorelease
message is placed into an NSAutoreleasePool
object. This NSAutoreleasePool
class is like a collection of these objects and goes one by one sending a release message when it's drained. It drains the pool when you're out of scope. Then, every object retain's count is decreased by 1. By using an autorelease as an alternative to a release message, you extend the object's lifetime, this time maybe even longer if the object is later retained or at least until the NSAutoreleasePool
class is drained. If you put an object into the same pool more than once, for each time, it will receive a release
message.
While into an environment with reference counting, Cocoa presumes there will always be an autorelease pool available; otherwise, objects that have received an autorelease
message won't get released. This practice will leak memory and generate proper warning messages.
At the beginning of a cycle of the event loop, an autorelease pool is created by the Application Kit (one of the Cocoa frameworks, also known as AppKit). It provides code to create and interact with GUI, and it's drained at the end of this cycle, then every autoreleased object created when processing an event is just released. It means you don't need to create the pools yourself as the Application Kit does it for you. However, if there are many autoreleased objects created by your application, you should consider the creation of "local" autorelease pools; this is an advantage to avoid the peak memory footprint.
To create an NSAutoreleasePool
object, you can use the regular alloc
and init
methods and use drain
to dismiss it. A pool cannot be retained; the consequences of drain
is like a deallocation, and it's very important to do so in the same context you created it.
Every thread has its own stack of autorelease pools. These stacks contain NSAutoreleasePool
objects, which in turn contain autoreleased objects. Every new autoreleased object is placed on the top of the pool and every new pool is placed on the top of the stack. A pool is removed from a stack when it's drained. Before a thread is finished, it drains every autorelease pool on its stack. Despite the fact that an autorelease pool can be manually created and objects can be manually added to it, ARC still drains the pool automatically: you're not allowed to do it yourself.
To ensure that you don't have to worry about ownership, this is what ARC does: easily create autorelease pools, and make them temporarily handle the holding and releasing of autoreleased objects for you.