NSAutoreleasePool
Inherits From: NSObject
Conforms To: NSObject (NSObject)
Declared In: Foundation/NSAutoreleasePool.h
Class Description
The Foundation Kit uses the NSAutoreleasePool class to implement NSObject's autorelease method. An autorelease pool simply contains other objects, and when deallocated, sends a release message to each of those objects. An object can be put into the same pool several times, and receives a release message for each time it was put into the pool.
You use autorelease pools to limit the time an object remains valid after it's been autoreleased (that is, after it's been sent an autorelease message or has otherwise been added to an autorelease pool). Autorelease pools are created using the usual alloc and init messages, and disposed of with release. An autorelease pool should always be released in the same context (invocation of a method or function, or body of a loop) that it was created. You should never send retain or autorelease messages to an autorelease pool.
Autorelease pools are automatically created and destroyed in OpenStep applications, so your code normally doesn't have to worry about them. There are two cases, though, where you should explicitly create and destroy your own autorelease pools. If you're writing a program that's not based on the Application Kit, such as a UNIX tool, there's no built-in support for autorelease pools; you must create and destroy them yourself. Also, if you need to write a loop that creates many temporary objects, you should create an autorelease pool in the loop to prevent too long a delay in the disposal of those objects.
Enabling the autorelease feature in a program that's not based on the Application Kit is easy. Many programs have a top-level loop where they do most of their work. To enable the autorelease feature you create an autorelease pool at the beginning of this loop and release it at the end. An autorelease message sent in the body of the loop automatically puts its receiver into this pool. The main() function might look like this:
int main(int argc, char *argv[])
{
int i;
/* Do whatever setup is needed. */
for (i = 0; i < argc; i++) {
NSAutoreleasePool *pool;
NSString *fileContents;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
fileContents = [[[NSString alloc] initWithContentsOfFile:argv[i]] autorelease];
processFile(fileContents);
[pool release];
}
/* Do whatever cleanup is needed. */
exit(EXIT_SUCCESS);
}
Any object autoreleased inside the for loop, such as the fileContents string object, is added to pool, and when pool is released at the end of the loop those objects are also released.
Note that autoreleasing doesn't work outside of the loop. This isn't a problem, since the program terminates shortly after the loop ends, and memory leaks aren't usually serious at that stage of execution. Your cleanup code shouldn't refer to any objects created inside the loop, though, since they may be autoreleased in the loop and therefore released as soon as it ends.
Nesting Autorelease Pools
You may need to manually create and destroy autorelease pools even in an application that uses the Application Kit if you write loops that create many temporary objects. For example, if you write a loop that iterates 1000 times and invokes a method that creates 15 temporary objects, those 15,000 objects will remain until the application's autorelease pool is deallocated, possibly well after they're no longer needed.
You can create your own autorelease pools within the loop to prevent these unwanted objects from remaining around. Autorelease pools nest themselves on a per-thread basis, so that if you create your own pool, it adds itself to the application's default pool, forming a stack of autorelease pools. Likewise, if you create another pool (within a nested loop, perhaps), it adds itself to the first pool you created. autorelease automatically adds its receiver to the last pool created, creating a nesting of autorelease contexts. The implications of this are described below.
A method that creates autorelease pools looks much like the main() function given above:
- (void)processString:(NSString *)aString
{
int i;
for (i = 0; i < 1000; i++) {
NSAutoreleasePool *subpool = [[NSAutoreleasePool alloc] init];
NSString *thisLine;
thisLine = [self lineNumbered:i fromString:aString];
/* Do some work with thisLine. */
[subpool release];
}
return;
}
If you assume that lineNumbered:fromString: returns a string object that's been autoreleased while subpool is in effect, that object is released with subpool at the end of the loop. The work involving thisLine may create other temporary objects, which are also released at the end of the loop. None of these objects remains outside of this loop or the processString: method (unless they've been retained).
Note that because an autorelease pool adds itself to the previous pool when created, it doesn't cause a memory leak in the face of an exception or other sudden transfer out of the current context. If an exception occurs in the above loop, or if the work in the loop involves immediately returning or breaking out of the loop, the sub-pool is released by the application's default pool (or whatever pool was in effect before the sub-pool was created), unwinding the autorelease-pool stack up to the one that's supposed to be active.
Guaranteeing the Foundation Ownership Policy
By manually creating an autorelease pool, you reduce the potential lifetime of temporary objects to the lifetime of that pool. After an autorelease pool is deallocated, you should regard as disposed of any object that was autoreleased while that pool was in effect, and not send a message to that object or return it to the invoker of your method. This method, for example, is incorrect:
- findMatchingObject:anObject
{
id match = nil;
while (match == nil) {
NSAutoreleasePool *subpool = [[NSAutoreleasePool alloc] init];
/* Do some searching that creates a lot of temporary objects.*/
match = [self expensiveSearchForObject:anObject];
[subpool release];
}
/* Danger!! The match object may not exist at this point! */
[match setIsMatch:YES forObject:anObject];
return match;
}
expensiveSearchForObject: is invoked while subpool is in effect, which means that match, which may have been autoreleased, is released at the bottom of the loop. Sending setIsMatch:forObject: after the loop could cause the application to crash. Similarly, returning match allows the sender of findMatchingObject: to send a message to it, also causing your application to crash.
If you must pull a temporary object out of a nested autorelease context, you can do so by retaining the object within the context and then autoreleasing it after the pool has been released. Here's a correct implementation of findMatchingObject::
- 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];
}
[match setIsMatch:YES forObject:anObject];
return [match autorelease]; /* Let match go and return it. */
}
By retaining match while subpool is in effect and autoreleasing it after the subpool has been released, match is effectively moved from subpool to the pool that was previously in effect. This gives it a longer lifetime and allows it to be sent messages outside the loop and to be returned to the invoker of findMatchingObject:.
General Exception Conditions
An NSInvalidArgumentException is raised on any attempt to send either retain or autorelease messages to an autorelease pool object.
Adding an Object to the Current Pool