Defensive Programming in Cocoa (from VTM_iPhone)

I’ve been attending the iPhone Developers Conference in Seattle hosted by Voices That Matter (check out VTM_iPhone for the Twitter stream), which has been really informative.  Easily the most informative session at the iPhone Developers Conference for me was Defensive Programming in Cocoa by Mike Ash.  On Mike’s blog he has a series called Friday Q&A and it appears that most of the material for his talk was taken from an article he posted in December with the same name.  Two sections really stand out for me.

Memory Management – Put all your memory management for member variables in the setter for that member variable rather than scattering it around the code.  So instead of doing this:

[_myMemberVar release];

do this:

self.myMemberVar = nil;

after implementing this:

@property (retain) MyClass * myMemberVar;
@synthesize myMemberVar = _myMemberVar;

or this:

- (void) setMyMemberVar: (MyClass *) myMemberVar
{
    if (_myMemberVar != myMemberVar)
    {
        [_myMemberVar release];
        _myMemberVar = [myMemberVar retain];
    }
}

Categories – Since someone could override a method in your category with their own method making your method impossible to call, name your methods in a way to reduce that possibility.  The converse is also true.  For example, if you define a method in a category for an Apple-provided class, Apple could later come along and define that method in a class category and then all their calls will go to your method instead of theirs.

Key-Value Observers – The only thing not covered in that article that he covered in his talk was something that I started applying immediately:  When implementing key-value observers, make sure your observer only handles the notification if it knows that it is for your class.  This is a problem because your observer could be overriding the superclass’s observer and your observer must call the superclass’s observer if the notification isn’t for you.

To solve this, always pass specific to your class as the context parameter to the addObserver: method.

static void * s_kClassContext = &s_kClassContext;
...
[obj addObserver: self
      forKeyPath: "foo"
         options: NSKeyValueObservingOptionNew
         context: s_kClassContext];
...
- (void) observeValueForKeyPath: (NSString *) keyPath
                       ofObject: (id) object
                         change: (NSDictionary *) change
                        context: (void *) context
{
    if (context == s_kClassContext)
    {
        if ([keyPath isEqual: @"location"])
        {
            ...
        }
    }
    else
    {
        // Notification is not for us.
        [super observeValueForKeyPath: keyPath
                             ofObject: object
                               change: change
                              context: context];
    }
}