Cocoa Programming for Mac OS X - Aaron Hillegass [29]
Here, then, are the rules. (Implementation details are in parentheses.)
• If you create an object by using a method whose name starts with alloc or new or contains copy, you have taken ownership of it. (That is, assume that the new object has a retain count of 1 and is not in the autorelease pool.) You have a responsibility to release the object when you no longer need it. Some of the common methods that convey ownership are alloc (which is always followed by an init method), copy, and mutableCopy.
• An object created through any other means, such as a convenience method, is not owned by you. (That is, assume that it has a retain count of 1 and is already in the autorelease pool and thus doomed unless it is retained before the autorelease pool is drained.)
• If you don’t own an object and want to ensure its continued existence, take ownership by sending it the message retain. (This increments the retain count.)
• When you own an object and no longer need it, send it the message release or autorelease. (The message release decrements the retain count immediately; autorelease causes the message release to get sent when the autorelease pool is drained.)
• As long as it has at least one owner, an object will continue to exist. (When its retain count goes to zero, it is sent the message dealloc.)
One of the tricks to understanding memory management is to think locally. The LotteryEntry class does not need to know anything about other objects that also care about its entryDate. As long as a LotteryEntry instance retains objects it wants to keep, you won’t have any problems. Programmers new to the language sometimes make the mistake of trying to keep tabs on objects throughout an application. Don’t do this. If you follow these rules and always think local to a class, you never have to worry what the rest of an application is doing with an object.
Accessor Methods
An object has instance variables. Other objects cannot access these variables directly. To enable other objects to read and set an instance variable, an object will usually have a pair of accessor methods.
For example, if a class Rex has an instance variable named fido, the class will probably have at least two other methods: fido and setFido:. The fido method enables other objects to read the fido variable; the setFido: method enables other objects to set the fido variable.
If you have a nonpointer type, the accessor methods are quite simple. For example, if your class has an instance variable called foo of type int, you would create the following accessor methods:
- (int)foo
{
return foo;
}
- (void)setFoo:(int)x
{
foo = x;
}
These methods will allow other objects to get and set the value of foo.
Matters become more complicated if foo is a pointer to an object. In the “setter” method, you need to make sure that the new value is retained and the old value released, as shown in Figure 4.5. If you assume that foo is a pointer to an NSDate, there are three common idioms in setter methods. All three work correctly, and you can probably find some experienced Cocoa programmers who will argue the superiority of any one of them. Each has trade-offs.
Figure 4.5. Before and After setFoo:
The first idiom is “Retain, Then Release”:
- (void)setFoo:(NSDate *)x
{
[x retain];
[foo release];
foo = x;
}
Here, it is important to retain before releasing. Suppose that you reverse the order. If x and foo are both pointers to the same object that happens to have a retain count of 1, the release would cause the object to be deallocated before it was retained. Trade-off: If they are the same value, this method performs an unnecessary retain and release.
The second idiom is “Check Before Change”:
- (void)setFoo:(NSDate *)x
{
if (foo != x) {
[foo release];
foo = [x retain];
}
}
Here, you are not setting the variable unless a different value is passed in. Trade-off: An extra if