This document contains information on fine-tuning your application’s handling of autorelease pools; see the document “Object Ownership and Disposal” for general information on using the autorelease mechanism.
Overview of Autorelease Pools
Autorelease Pools in Non-AppKit Programs
Autorelease Pools and Threads
Scope of Autorelease Pools and Implications of Nested Autorelease Pools
Guaranteeing the Foundation Ownership Policy
Garbage Collection
An autorelease pool is an instance of NSAutoreleasePool
that “contains” other objects that have received an autorelease
message; when the autorelease pool is deallocated it sends a release
message to each of those objects. An object can be put into an autorelease pool several times, and receives a release
message for each time it was put into the pool. Thus, sending autorelease
instead of release
to an object extends the lifetime of that object at least until the pool itself is released (the object may survive longer if it is retained in the interim).
Cocoa always expects there to be an autorelease pool available. If a pool is not available, autoreleased objects do not get released and you leak memory. If you send an autorelease message when a pool is not available, Cocoa logs a suitable error message.
You create an NSAutoreleasePool
object with the usual alloc
and init
messages, and dispose of it with release
or drain
(an exception is raised if you send autorelease
or retain
to an autorelease pool)—to understand the difference between release
or drain
, see “Garbage Collection.” An autorelease pool should always be released in the same context (invocation of a method or function, or body of a loop) in which it was created.
Autorelease pools are arranged in a stack, although they are commonly referred to as being "nested." When you create a new autorelease pool, it is added to the top of the stack. When pools are deallocated, they are removed from the stack. When an object is sent an autorelease
message, it is added to the current topmost pool for the current thread.
The ability to nest autorelease pools means that you can include them in any function or method. For example, a main
function may create an autorelease pool and call another function that creates another autorelease pool. Or a single method might have an autorelease pool for an outer loop, and another autorelease pool for an inner loop. The ability to nest autorelease pools is a definite advantage, but there are side effects when exceptions occur (see “Scope of Autorelease Pools and Implications of Nested Autorelease Pools”).
The Application Kit automatically creates a pool at the beginning of an event cycle (or event-loop iteration), such as a mouse down event, and releases it at the end, so your code normally does not have to worry about them. There are three cases, though, where you might create and destroy your own autorelease pools:
If you are writing a program that is not based on the Application Kit, such as a command-line tool, there is no built-in support for autorelease pools; you must create and destroy them yourself.
If you spawn a secondary thread, you must create your own autorelease pool as soon as the thread begins executing; otherwise, you will leak objects. (See “Autorelease Pools and Threads” for details.)
If you write a loop that creates many temporary objects, you may create an autorelease pool inside the loop to dispose of those objects before the next iteration. This can help reduce the maximum memory footprint of the application.
Autorelease pools are used "in line". There should typically be no reason why you should make an autorelease pool an instance variable of an object.
Enabling the autorelease mechanism in a program that is not based on the Application Kit is easy. You can simply create an autorelease pool at the beginning of the main()
function, and release it at the end—this is the pattern used by the Foundation Tool template in Xcode. This establishes a pool for the lifetime of the task. However, this also means that any autoreleased objects created during the lifetime of the task are not disposed of until the task completes. This may lead to the task's memory footprint increasing unnecessarily. You can also consider creating pools with a narrower scope.
Many programs have high-level loops where they do much of their work. To enable the autorelease mechanism you can create an autorelease pool at the beginning of an iteration through this loop and release it at the end.
Your main
function might look like the code in Listing 1.
Listing 1 Example of a main
function for a non-AppKit program
void main() |
{ |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
NSArray *args = [[NSProcessInfo processInfo] arguments]; |
unsigned count, limit = [args count]; |
for (count = 0; count < limit; count++) |
{ |
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; |
NSString *fileContents; |
NSString *fileName; |
fileName = [args objectAtIndex:count]; |
fileContents = [[[NSString alloc] initWithContentsOfFile:fileName] autorelease]; |
// this is equivalent to using stringWithContentsOfFile: |
/* Process the file, creating and autoreleasing more objects. */ |
[loopPool release]; |
} |
/* Do whatever cleanup is needed. */ |
[pool drain]; |
exit (EXIT_SUCCESS); |
} |
This program processes files passed in on the command line. The for
loop processes one file at a time. An NSAutoreleasePool
object is created at the beginning of this loop and released at the end. Therefore, any object sent an autorelease
message inside the loop (such as fileContents
) is added to loopPool
, and when loopPool
is released at the end of the loop those objects are also released. Additionally, any autoreleased objects created in the context of the for
loop (such as fileName
) are released when loopPool
is released even if they’re not explicitly sent an autorelease
message.
Each thread in a Cocoa application maintains its own stack of NSAutoreleasePool
objects. When a thread terminates, it automatically releases all of the autorelease pools associated with itself. Autorelease pools are automatically created and destroyed in the main thread of applications based on the Application Kit, so your code normally does not have to deal with them there. If you are making Cocoa calls outside of the Application Kit's main thread, however, you need to create your own autorelease pool. This is the case if you are writing a Foundation-only application or if you detach a thread.
If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should periodically destroy and create autorelease pools (like the Application Kit does on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.
Note: If you create secondary threads using the POSIX thread APIs instead of NSThread, you cannot use Cocoa—including NSAutoreleasePool
—unless Cocoa is in multithreading mode. Cocoa enters multithreading mode only after detaching its first NSThread
object. To use Cocoa on secondary POSIX threads, your application must first detach at least one NSThread
object, which can immediately exit. You can test whether Cocoa is in multithreading mode with the NSThread
class method isMultiThreaded
.
It is common to speak of autorelease pools as being nested because of the enclosure evident in code, as illustrated in Listing 1. But you can also think of nested autorelease pools as being on a stack, with the “innermost” autorelease pool being on top of the stack. As noted earlier, this is actually how nested autorelease pools are implemented: Each thread in a program maintains a stack of autorelease pools. When you create an autorelease pool, it is pushed onto the top of the current thread’s stack. When an object is autoreleased—that is, when an object is sent an autorelease
message or when it is passed as the argument to the addObject:
class method—it is always put in the autorelease pool at the top of the stack.
The scope of an autorelease pool is therefore defined by its position in the stack and the simple fact of its existence. The topmost pool is the pool to which autoreleased objects are added. If another pool is created, the current topmost pool effectively goes out of scope until the new pool is released (at which point the original pool once again becomes the topmost pool). It (obviously) goes out of scope permanently when it is itself released.
If you release an autorelease pool that is not the top of the stack, this causes all (unreleased) autorelease pools above it on the stack to be released, along with all their objects. If you neglect to send release
to an autorelease pool when you are finished with it (something not recommended), it is released when one of the autorelease pools in which it nests is released.
This behavior has implications for exceptional conditions. If an exception occurs, and the thread suddenly transfers out of the current context, the pool associated with that context is released. However, if that pool is not the top pool on the thread’s stack, all the pools above the released pool are also released (releasing all their objects in the process). The top autorelease pool on the thread’s stack then becomes the pool previously underneath the released pool associated with the exceptional condition. Because of this behavior, exception handlers do not need to release objects that were sent autorelease
. Neither is it necessary or even desirable for an exception handler to send release
to its autorelease pool, unless the handler is re-raising the exception.
By creating an autorelease pool instead of simply releasing objects, you extend the lifetime of temporary objects to the lifetime of that pool. After an autorelease pool is deallocated, you should regard any object that was autoreleased while that pool was active as “disposed of”, and not send a message to that object or return it to the invoker of your method.
If you must use a temporary object beyond an autorelease context, you can do so by sending a retain
message to the object within the context and then send it autorelease
after the pool has been released as in:
– findMatchingObject:anObject |
{ |
id match = nil; |
while (match == nil) { |
NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init]; |
/* Do a search that creates a lot of temporary objects. */ |
match = [self expensiveSearchForObject:anObject]; |
if (match != nil) { |
[match retain]; /* Keep match around. */ |
} |
[subPool release]; |
} |
return [match autorelease]; /* Let match go and return it. */ |
} |
By sending retain
to match
while subpool
is in effect and sending autorelease
to it after subpool
has been released, match
is effectively moved from subpool
to the pool that was previously active. This extends the lifetime of match
and allows it to receive messages outside the loop and be returned to the invoker of findMatchingObject:
.
Although the garbage collection system (Garbage Collection Programming Guide) does not use autorelease pools per se, autorelease pools can be useful in providing hints to the collector if you are developing a hybrid framework (that is, one that may be used in garbage-collected and reference-counted environments).
Autorelease pools are released when you want to relinquish ownership of the objects that have been added to the pool. This frequently has the effect of disposing of temporary objects that have accumulated up to that point—for example, at the end of the event cycle, or during a loop when you create a large number of temporary objects. These are typically also points at which it might be useful to hint to the garbage collector that collection is likely to be warranted.
In a garbage collected environment, release
is a no-op. NSAutoreleasePool
therefore provides a drain
method that in a reference-counted environment behaves the same as calling release
, but which in a garbage collected environment triggers garbage collection (if the memory allocated since the last collection is greater than the current threshold). Typically, therefore, you should use drain
rather than release
to dispose of an autorelease pool.
© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-05-06)