I’ve been writing some code that uses the delegate pattern, and it was getting a little ugly and beginning to smell. In case you’re not familiar writing your own delegates, here is the standard technique to call a delegate method of a class:

- (void) someMethod {
    if ([delegate respondsToSelector: @selector(operationShouldProceed)]) {
        if ([delegate operationShouldProceed]) {
            // do something appropriate
        }
    }
}

This class had quite a few delegates, and the respondsToSelector: check was affecting readability. It’s also theoretically faster to cache the check in an ivar, so it’s not called everytime. I’ve used delegates before, but I wanted to explore my options, this time. In my quest to optimize for readability and performance, I got a few surprises. I’ll go over the steps, and the final results of my investigation.

The first change was to pull the respondsToSelector: check out into it’s own method. I used the same method signature as the actual delegate, for simplicity:

- (void) someMethod {
    if ([self operationShouldProceed]) {
        // do something appropriate
    }
}

- (BOOL) operationShouldProceed {
    if ([delegate respondsToSelector: @selector(operationShouldProceed)])
        return [delegate operationShouldProceed];
    else
        return DEFAULT_RETURN;
}

This now makes someMethod a bit cleaner. Imagine multiple delegate methods called in a row, and the cleanliness multiplies as the nested if statements disappear. This was a big win for my code. One neat bit of information I learned here, was to use the _cmd hidden argument. This was another benefit to using the same method signature as the actual delegate method:

- (BOOL) operationShouldProceed {
    if ([delegate respondsToSelector: _cmd])
        return [delegate operationShouldProceed];
    else
        return DEFAULT_RETURN;
}

Okay, this satisfied my readability optimization. Next, I tried to tackle the performance optimization. The obvious optimization is to create a separate BOOL ivar per delegate method, and then set them when the delegate is set. This was pretty easy to implement, so I wrote up a quick test to benchmark how much faster this would be. It turns out the standard technique is about 2.4 times slower than the ivar cache. And my readability optimization is about 2.9 times slower than the ivar cache. Keep in mind, none of these methods were actually slow… we’re talking millions of messages per second here, doing nothing other than calling a method in a tight loop of 100,000,000 iterations. So 3 times slower isn’t so bad, when you take into account a real situation, where many other methods are actually being called.

I thought about this for a bit, and for lots of delegate methods that’s lots of ivars. I thought I could improve upon this by encapsulating all the BOOL ivars into separate class. To make it generic, I could use a dictionary of booleans, indexed by selector. When a delegate method was called, I’d first lookup the selector in the dictionary to see if we knew if it responded to the selector. I’d only call respondsToSelector: if the dictionary lookup failed.

As I started writing the code for this, I remembered that it was not possible to store booleans in a dictionary. Okay, that’s what NSNumber is for. I also realized you can not use a selector as a key since SEL isn’t an object. Okay, that’s what NSValue is for. So, now that I had my wonderful optimization written, I wanted to benchmark my results to prove to the world just how smart I was. It turns out I wasn’t smart, afterall. This “optimization” was 23 times slower than the standard technique and 56 times slower than the ivar cache technique. Okay, so forget that. Creating the wrapper objects and the dictionary lookup is actually slower than calling respondsToSelector:. Since this technique is no more readable than my separate method technique, there’s no need for added complexity, more code, and a big performance hit for no gain in readability.

So, in conclusion, I’d say the standard delegate technique is fine for most situations. If the respondsToSelector: check gets in the way of readability, refactor that out into it’s own method. Don’t worry about caching the results of respondsToSelector:. You probably won’t notice any difference.

Update 2007-01-26: I wrote a followup article investigating a couple more delegatation techniques.