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
.