Cocoa Programming for Mac OS X - Aaron Hillegass [28]
Autorelease pools simplify releasing objects. You can add an object to the current autorelease pool simply by sending it the message autorelease. Adding an object to an autorelease pool marks it to be sent a release message at some point in the future.
The release message is sent once the pool is drained. In a Cocoa application, an autorelease pool is created before every event is handled and is drained after the event has been handled. Thus, unless the objects in the autorelease pool are being retained, they will be destroyed as soon as the event has been handled.
In the case of the lottery project, a command-line tool, there is no event loop, and so the autorelease pool has been created explicitly. This hints at another aspect of autorelease pools: They can be nested to reduce peak memory consumption, for example, in a large loop. The topmost pool is the pool to which autoreleased objects will be sent.
Note that if you autorelease an object n times, it will be sent release n times once the pool is drained.
Autoreleased Objects Are Useful
One correct solution to our problem is then to autorelease the string before we return it:
- (NSString *)description
{
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setTimeStyle:NSDateFormatterNoStyle];
[df setDateStyle:NSDateFormatterMediumStyle];
NSString *result;
result = [[NSString alloc] initWithFormat:@"%@ = %d and %d",
[df stringFromDate:entryDate],
firstNumber, secondNumber];
[result autorelease];
[df release];
return result;
}
You can think of autoreleasing as an alternative to directly releasing an object. Sometimes, Objective-C programmers autorelease objects out of necessity, such as in this case, when returning an object from a method; other times, it is more a matter of convenience.
Because you will frequently need objects that you are not retaining, many classes have class methods that return autoreleased objects. NSString, for example, has stringWithFormat:. The simplest correct solution then would be:
- (NSString *)description
{
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setTimeStyle:NSDateFormatterNoStyle];
[df setDateStyle:NSDateFormatterMediumStyle];
NSString *result;
result = [NSString stringWithFormat:@"%@ = %d and %d",
[df stringFromDate:entryDate],
firstNumber, secondNumber];
[df release];
return result;
}
Autoreleased Objects Are Convenient
Recall that an autoreleased object won’t be released until the pool is drained (usually when the current cycle of the event loop ends). This behavior makes it perfect for providing an intermediate result. For example, if you had an array of NSString objects, you could create a string with all the elements in uppercase and concatenated together, like this:
- (NSString *)concatenatedAndAllCaps
{
int i;
NSString *sum = @"";
NSString *upper;
for (i=0; i < [myArray count]; i++) {
upper = [[myArray objectAtIndex:i] uppercaseString];
sum = [NSString stringWithFormat:@"%@%@", sum, upper];
}
return sum;
}
With this method, if you have 13 strings in the array, 26 autoreleased strings will be created (13 by uppercaseString and 13 by stringWithFormat:; the initial constant string is a special case and doesn’t count). One of the resulting strings is returned and may be retained by the object that asked for it. The other 25 strings are deallocated automatically at the end of the event loop. (Note that you would probably get better performance in this example by appending the uppercased string to an NSMutableString instead of creating a new string and adding it to the autorelease pool each time through the loop.)
The Retain-Count Rules
Now that you are familiar with retain, release, and autorelease, you are ready for the rules.
In these rules, we use the word “you” to mean “an instance of whatever class you are currently working on.” It is a useful form of empathy: You imagine that you are the object you are writing. So, for example, “If you retain the string, it will not be deallocated” really