Jan 2009

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.)

Comments

git & less

For the UNIX users out there who use the git revision control system with the oldskool less pager, try adding the following to your ~/.gitconfig file:

[core]
    # search for core.pager in
    # <http://www.kernel.org/pub/software/scm/git/docs/git-config.html>
    # to see why we use this convoluted syntax
    pager = less -$LESS -SFRX -SR +'/^---'

That’ll launch less with three options set:

  • -S: chops long lines rather than folding them (personal preference),
  • -R: permits ANSI colour escape sequences so that git’s diff colouring still works, and
  • +'/^---': sets the default search regex to ^--- (find --- at the beginning of the line), so that you can easily skip to the next file in your pager with the n key.

The last one’s the handy tip. I browse commits using git diff before committing them, and like being able to jump quickly back and forth between files. Alas, since less is a dumb pager and doesn’t understand the semantics of diff patches, we simply set the find regex to ^---, which does what we want.

Of course, feel free to change the options to your heart’s content. See the less(1) manpage for the gory details.

As the comment in the configuration file says, you’ll need to use the convoluted less -$LESS -SFRX prefix due to interesting git behaviour with the LESS environment variable:

Note that git sets the LESS environment variable to FRSX if it is unset when it runs the pager. One can change these settings by setting the LESS variable to some other value. Alternately, these settings can be overridden on a project or global basis by setting the core.pager option. Setting core.pager has no affect on the LESS environment variable behaviour above, so if you want to override git’s default settings this way, you need to be explicit. For example, to disable the S option in a backward compatible manner, set core.pager to "less -+$LESS -FRX". This will be passed to the shell by git, which will translate the final command to "LESS=FRSX less -+FRSX -FRX".

(And sure, I could switch to using a different pager, but I’ve been using less for more than a decade. Yep, I know all about Emacs & Vim’s diff-mode and Changes.app. It’s hard to break old habits.)

Comments