Setters are more straightforward than getters, because you don’t need to worry about memory management.
The best practice is to let the compiler write getters for you, by using Declared Properties.
But when I have to implement a getter manually, I prefer the (to my knowledge) safest pattern,
- (TypeOfX*) x;
{
return [[x retain] autorelease];
}
Note that by convention in Objective-C, a getter for the variable jabberwocky
is simply called jabberwocky
, not getJabberwocky
.
Why retain
Then autorelease
Basically return [[x retain] autorelease];
guarantees that what the getter returns will be valid for as long as any local objects in the code that called the the getter.
Consider,
NSString* oldName = [person name];
[person setName:@"Alice"];
NSLog(@"%@ has changed their name to Alice", oldName);
If -setName:
immediately release
es the value that -name
returned, oldName
will be invalid when it’s used in NSLog
. But if the implementation of [x name]
used retain/autorelease
, then oldName
would still be valid, because it would not be destroyed until the autorelease pool around the NSLog
was drained.
Also, autorelease pools are per thread; different threads have different autorelease pools that are drained at different times. retain/autorelease
makes sure the object is on the calling thread’s pool.
If this cursory explanation isn’t enough, read Seth Willitis’ detailed explanation of retain/autorelease
. I’m not going to explain it further here, because he’s done such a through job of it.
Ugly
return [[x retain] autorelease];
is more complicated, and harder to understand then a simple return x;
. But sometimes that ugliness is necessary, and the best place to hide it is in a one-line getter method. It’s self documenting. And once you understand Cocoa memory management, it’s entirely clear what the method does. For me, the tiny readability cost is worth the safety guarantee.
Big
return [[x retain] autorelease];
increases peak memory pressure, because it can defer dealloc
-ing unused objects until a few autorelease pools are drained. Honestly I’ve never measured memory usage, and found this to be a significant problem. It certainly could be, especially if the thing being returned is a large picture or chunk of data. But in my experience, it’s nothing to worry about for getters that return typical objects, unless there are measurements saying otherwise.
Slow
return [[x retain] autorelease];
is obviously slower then just return x;
. But I doubt that optimizing an O(1) getter is going to make a significant difference to your application’s performance — especially compared to other things you could spend that time optimizing. So until I have data telling me otherwise, I don’t worry about adding an the extra method calls.
This is a Good Rule to Break
As I mentioned before, getters don’t need to worry about memory management. It could be argued that the return [[x retain] autorelease];
pattern is a premature optimization of theoretical safety at the expense of concrete performance.
Good programmers try to avoid premature optimization; so perhaps I’m wrong to follow this “safer” pattern. But until I have data showing otherwise, I like to do the safest thing.
How do you write getters, and why?