Vincent Gable’s Blog

March 31, 2009

How To Write Cocoa Object Getters

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 releasees 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?

8 Comments »

  1. Wouldn’t the following code be considered better:
    NSString* oldName = [[person name] retain];
    [person setName:@"Alice"];
    NSLog(@”%@ has changed their name to Alice”, oldName);
    [oldName release];

    Comment by GaViT — April 17, 2009 @ 3:38 pm

  2. @GaViT,

    I do not prefer that style, because it’s doing extra memory management that’s irrelevant to what the code is actually doing (renaming someone “Alice”). That means more for me to puzzel through when I’m trying to figure out what’s going on.

    I’d rather have a getter or setter that can’t let the problem happen. (Using autorelease in your setter works if there’s only one thread).

    If I were not relying on my setter being clever, (and there are good arguments for doing that) I would do NSString *oldName = [[[person name] retain] autorelease]; instead of calling release when I’m done with oldName. That way I don’t have to think about when it’s the right time to release oldName, and more importantly I can’t do it at the wrong time, (and I can’t come back and change the code later in such a way that it becomes the wrong time).

    Comment by Vincent Gable — April 17, 2009 @ 5:12 pm

  3. While I do agree that you can’t do it at the wrong time, I don’t think it is more of a hassle to figure out when a good time is to release it(when you are done with the variable), nor do I think NSString* oldName = [[person name] retain]; is more irrelevant to what the code is doing than return [[x retain] autorelease]

    Comment by GaViT — April 18, 2009 @ 9:50 am

  4. nor do I think NSString* oldName = [[person name] retain]; is more irrelevant to what the code is doing than return [[x retain] autorelease]

    Yes, the memory management is irrelevant in both cases, but

    1. return [[x retain] autorelease] only happens in exactly one spot.
    2. That one spot is a one-line getter method. There’s not a lot of code that it confuses.
    3. It’s use follows a pattern, so you don’t have to think about it when you see it. (Any time you see a naked retain without some pattern justifying it, you have to figure out why — or if it’s a memory leak).

    Comment by Vincent Gable — April 18, 2009 @ 5:16 pm

  5. While I do agree that you can’t do it at the wrong time, I don’t think it is more of a hassle to figure out when a good time is to release it(when you are done with the variable)

    That’s infinitely more work then not having to decide when you are done with something. And having to decide if you are still done with something there, every time you modify the code. And having to fix the bugs that Murphy’s Law says will happen.

    Yes, in a 4 line example it’s not hard to balance retain/release; and it’s not hard much of the time. But manually malloc()/free()-ing memory isn’t hard in a 4 line example, or always difficult. But there are simpler ways, and I think it’s making life unnecessarily difficult not to use them when possible.

    Comment by Vincent Gable — April 18, 2009 @ 5:35 pm

  6. I use @synthesize and let Apple do it the best way possible, given context.

    If in a non-GC’d environment, specifying an @property as nonatomic just returns the underlying object value.

    Comment by bbum — May 18, 2009 @ 1:14 am

  7. Shouldn’t your article begin with “Getters are more straightforward than setters”, instead of “Setters are more straightforward than getters, because you don’t need to worry about memory management.

    Comment by Ricky Helgesson — April 27, 2012 @ 3:14 am

  8. Hi guys and gals ! How are you ? I am new here, pleaze welcome me.

    Comment by felteklyKal — June 4, 2013 @ 9:34 pm

RSS feed for comments on this post.

Leave a comment

Powered by WordPress