Objective-C 2.0 Accessors & Memory Management

Quite often, you may have simple setter methods that need to do a just a tiny bit of work before or after setting an instance variable. For example, maybe you need to redraw something after setting the property of an object. So, instead of writing this:

    [self setBackgroundColor:[NSColor blueColor]];
    [view setBackgroundColor:[NSColor blueColor]];

You’d probably want to move the relevant code to your -setBackgroundColor: accessor instead:

    - (void)setBackgroundColor:(NSColor*)color
    {
        // Assuming that _backgroundColor is the ivar you want to set
        if(_backgroundColor != color)
        {
            [_backgroundColor release];
            _backgroundColor = [color retain];
    
            // Update the view's background color to reflect the change
            [view setBackgroundColor:_backgroundColor];
        }
    }

Then you can simply call -setBackgroundColor: and expect it all to work nicely:

    // -setBackgroundColor: updates the view's background color
    // automatically now
    [self setBackgroundColor:[NSColor blueColor]];

(You could use Key-Value Observing to do this, but I generally avoid KVO for simple intra-class property dependencies like this. I don’t think the overhead of maintaining all the KVC dependencies and KVO-related methods is worth the cost.)

Of course, the above method requires that you write all that stupid boilerplate memory management code in the accessor. Instead of doing that, I tend to declare a private _backgroundColor property in the class, @synthesize a method for the private property, and then use the private property’s generated accessors instead:

    @interface MyClass ()
    
    // Declare a _private_ _backgroundColor property (thus the underscore
    // in front, and why it's declared in a class continuation rather than
    // in the public header)
    @property (copy, setter=_setBackgroundColor:) NSColor* _backgroundColor;
    
    @end
    
    //
    
    @implementation MyClass
    
    @synthesize _backgroundColor;
    
    - (NSColor*)backgroundColor
    {
        return [self _backgroundColor];
    }
    
    - (void)setBackgroundColor:(NSColor*)color
    {
        // Use the private property to set the background colour, so it
        // handles the memory management bollocks
        [self _setBackgroundColor:color];
    
        [view setBackgroundColor:[self _backgroundColor]];
    }
    
    ...
    
    @end

With that technique, it’s possible to completely directly setting ivars, and thus avoid -retain and -release altogether. (You’ll still need to use -autorelease at various times, of course, but that’s reasonably rare.) We have some source code files that are well over 2000 lines of code without a single explicit [_ivar retain]; or [_ivar release]; call thanks to this technique. (Yeah, 2000 lines is also large and the class needs refactoring, but that’s another story.)

Of course, you could just use garbage collection which avoids 99% of the need for this bollocks:

    - (void)setBackgroundColor:(NSColor*)color
    {
        // Yay GC!
        self->_backgroundColor = color;
    
        [view setBackgroundColor:self->_backgroundColor];
    }

But plenty of us don’t have that luxury yet. (iPhone, ahem.)

blog comments powered by Disqus