Why shouldn't I use the getter to release a property in objective-c?

I was told by a fellow StackOverflow user that I should not use the getter method when releasing a property:

@property(nonatmic, retain) Type* variable;
@synthesize variable;

// wrong
[self.variable release]; 

// right
[variable release]; 

He did not explain in detail why. They appear the same to me. My iOS book said the getter on a property will look like this:

- (id)variable {
    return variable;
}

So doesn't this mean [self variable], self.variable, and variable are all the same?


Solution 1:

For a retained property with no custom accessor, you can release the object by:

self.variable = nil;

This has the effect of setting the ivar (which may not be called 'variable' if you have only declared properties) to nil and releasing the previous value.

As others have pointed out, either directly releasing the ivar (if available) or using the method above is OK - what you must not do is call release on the variable returned from a getter.

Solution 2:

You can optionally write custom getter behavior, which may result in completely different behavior. So, you cannot always assume that [variable release] has the same results as [self.variable release].

As well, you can write custom properties without an exclusive ivar backing them... it can get messy fast if you start releasing objects from references returned by getters!

There may be additional reasons that I'm unaware of...

Solution 3:

A typical getter will look more like this:

- (id)variable {
   return [[variable retain] autorelease];
}

So if you use [self.variable release] you have an additional retain and autorelease that you don't really need when you just want to release the object and that cause the object to be released later than necessary (when the autorelease pool is drained).

Typically, you would either use self.variable = nil which has the benefit that it also sets the variable to nil (avoiding crashes due to dangling pointers), or [variable release] which is the fastest and may be more appropriate in a dealloc method if your setter has custom logic.

Solution 4:

not all getters take this form:

- (id)variable { return variable; }

...that is merely the most primitive form. properties alone should suggest more combinations, which alter the implementation. the primitive accessor above does not account for idioms used in conjunction with memory management, atomicity, or copy semantics. the implementation is also fragile in subclass overrides.

some really brief examples follow; things obviously become more complex in real programs where implementations become considerably more complex.

1) the getter may not return the instance variable. one of several possibilities:

- (NSObject *)a { return [[a copy] autorelease]; }

2) the setter may not retain the instance variable. one of several possibilities:

- (void)setA:(NSObject *)arg
{
  ...
  a = [arg copy];
  ...
}

3) you end up with memory management implementation throughout your program, which makes it difficult to maintain. the semantics of the class (and how it handles instance variables' ref counting) should be kept to the class, and follow conventions for expected results:

- (void)stuff:(NSString *)arg
{
    const bool TheRightWay = false;
    if (TheRightWay) {
        NSMutableString * string = [arg mutableCopy];
        [string appendString:@"2"];
        self.a = string;
        [string release];
        // - or -
        NSMutableString * string = [[arg mutableCopy] autorelase];
        [string appendString:@"2"];
        self.a = string;
    }
    else {
        NSMutableString * string = [arg mutableCopy];
        [string appendString:@"2"];
        self.a = string;
        [self.a release];
    }
}

failing to follow these simple rules makes your code hard to maintain and debug and painful to extend.

so the short of it is that you want to make your program easy to maintain. calling release directly on a property requires you to know a lot of context of the inner workings of the class; that's obviously bad and misses strong ideals of good OOD.

it also expects the authors/subclassers/clients to know exactly how the class deviates from convention, which is silly and time consuming when issues arise and you have to relearn all the inner details when issues arise (they will at some point).

those are some trivial examples of how calling release on the result of a property introduces problems. many real world problems are much subtler and difficult to locate.