Rules of Retention and Release
Once you know how to retain and release Cocoa objects, you need to become familiar on when to use the [retain], [release], and [autorelease] messages. Incorrect use of these messages can result in memory leaks or the dreaded SIG_SEGV and EXC_BAD_ACCESS fatal errors. These rules may help:
- If you instantiated an object using
[alloc]or[copy],dispose of the object using either[release]or an[autorelease]. - If you instantiated an object using a convenience method, the object is already slated for autorelease. Do not send a
[release]or an[autorelease]message to this object. - If you send a
[retain]message to the object, send a[release]message to cancel the retention. - If you received an object as a result of a method call, always assume that the object is valid within the scope of your calling method. If you want to extend the object's validity beyond the scope of your calling method, send a
[retain]message to that object. - Use the
[release]message whenever possible for performance reasons. If you are returning an object as part of your method call, use the[autorelease]message. - Retain and release the object to prevent the object from being accidentally eliminated as a side effect of certain Cocoa operations such as
NSLog(). - Balance all
[retain]messages with an equal number of[release]and[autorelease]messages. - Never send a
[dealloc]message to the object. Though this may also dispose of the object, it does so regardless of the current reference count. Other objects that have retained the object you've just deallocated are left with invalid references.
Designing Accessors and Modifiers
Accessor and modifier methods let other objects access and modify an object's internal properties. If the property itself happens to contain a Cocoa object, other Cocoa objects may try to retain and release that property value. This is where memory management becomes a factor in their implementation. Because accessors and modifiers are some of the most frequently called methods, be sure that these methods perform as efficiently as possible.
I design accessor and modifier methods in five different ways. To illustrate each design variant, I use the generic Cocoa class in Listing Three.
Listing Three
@interface anObj:NSObject
{
NSObject *theProp
}
-(NSObject *) getObject;
-(void) setObject:(NSObject *)newObj;
@end
The class has an accessor method named getObj and a modifier named setObject. The internal property used to store a Cocoa object is theProp. Listing Four is a basic design of an accessor and modifier method. Here, the getObject accessor simply returns a reference to theProp. The setObject modifier first sends a [retain] message to newObj to retain the new object. It then sends a [release] message to theProp to dispose of the object stored in that property. It then updates theProp with the object contained in newObj.
Listing Four
-(void) setObject:(NSObject *)newObj
{
[newObj retain];
[theObj release];
theProp = newObj;
}
-(NSObject *)getObject
{
return (theProp);
}
In most situations, this basic accessor and modifier implementation is sufficient. However, there are design variants that provide some performance advantages in certain design scenarios. For instance, if the calling method maintains its own autorelease pool, I use the getObject accessor in Listing Five. This accessor ensures that the returned object stored in theProp is automatically placed in the caller's autorelease pool.
Listing Five
-(NSObject *) getObject
{
return ([[theProp retain] autorelease]);
}
Listing Six is a faster variant of the setObject modifier.
Listing Six
-(void) setObject:(NSObject *)newObj
{
if (theProp != newObj)
{
[newObj retain];
[theProp release];
theProp = newObj;
}
}
Here, the argument newObj is compared with the property theProp. If both objects are different, the modifier retains newObj and releases theProp. Only then does it update theProp with newObj. By first comparing newObj and theProp, I avoid the unnecessary process of retaining and releasing the objects involved. I also avoid updating theProp if both the property and argument already contain the same object.
Listing Seven is a third variation of the setObject modifier.
Listing Seven
-(void) setObject:(NSObject *)newObj
{
if (theProp != newObj)
{
[theProp autorelease];
theProp = [newObj retain];
}
}
Here, an [autorelease] message is first sent to theProp. Afterwards, a [retain] message is sent to newObj, which is then assigned to theProp. I use this variant when my calling method has stored the previous value of theProp locally (see Listing Eight). Here, the current object stored in theProp is stored in the local theObj. A new object is then assigned to theProp using the [setObject] modifier. For the comparison to work, theObj needs to remain valid after the modifier is invoked. If a [release] message is sent to theProp instead of the [autorelease] message, the object stored in theObj may get eliminated, causing the if() construct to fail with either a SIG_SEGV or EXC_BAD_ACCESS error.
Listing Eight
NSObject *theObj; theObj = [anObj getObject]; [anObj setObject:[NSObject new]]; if (theObj == [anObj getObject) NSLog(@"Both objects are the same"); else NSLog(@"Both objects are different");
Listing Nine is a variation of the setObject modifier.
Listing Nine
-(void) setObject:(NSObject *)newObj
{
[theProp autorelease];
theProp = [newObj retain];
}
This modifier is a faster variant of the one back in Listing Seven. It omits the comparison check used to see theProp already has the same object as newObj. I use this variant if and only if I know that newObj will always be different from theProp.


