Introducing the Default Delegate design pattern

I recently posted an overview of callback mechanisms in Objective-C, including the pros and cons of delegates versus target/action pairs; in this post I’ll introduce a new design pattern that combines many of the benefits of both of these approaches into a single technique.

If you’re building a class to be reused many times, it can be nice to give your users (other coders) the flexibility to choose how they use their callbacks.  The default delegate pattern allows users to adopt a standard protocol and act as a standard delegate, or to implement the required methods which are registered via a target/action callback.  And it makes implementation essentially as easy as the delegate method, which is pretty easy as far as callbacks go.

[Note: links to the DefaultDelegate source are at the bottom of this post.]

What the default delegate can do

Here’s some demo user code that takes advantage of both modes (delegate and target/action) of a class called

MyClass

:

Here’s the debug output from the above demo code:

This behavior is useful whenever the end-users of your library may have a variety of use cases. Some of these use cases may work best with delegates, for example if there is a 1-1 relationship between your class and delegates. Other use cases may have a many-to-one relationship between your class and callbacks, which works better with the target/action mechanism. In my case, I’m improving an HTTPHelper class which I’d like to be very flexible, but also have a minimalistic interface. I needed a callback on a class method (aka static function in C++-speak), which is not possible with protocols, but I didn’t want to drop the protocol I already had. The DefaultDelegate object was a great solution.

Writing a class with a default delegate

You might guess that it’s tricky to get this dual behavior to work – and, in some ways, it is. But all the heavy lifting is encapsulated in the DefaultDelegate object, so that all other implementors of this pattern get out of jail free. Check out how easy it was to write the MyClass interface and implementation:

How it works

A DefaultDelegate object is written to understand a protocol given to it at runtime, which is made possible by the comprehensive Objective-C runtime library. When you use the pattern, your init method hands the protocol (using the

@protocol

keyword to get the appropriate pointer) to the DefaultDelegate. You also set your standard delegate pointer to the DefaultDelegate.

The user of your class will do one of two things: either assign target/action pairs to the default delegate, or adopt your delegate protocol and set your standard delegate pointer to their self.

Case 1: They use target/action pairs. Then DefaultDelegate keeps track of each target/action pair, and the corresponding selector in the delegate that’s being replaced. Your class calls the standard delegate object as always, but since it’s really a DefaultDelegate object, it intercepts the message, and passes it through to the correct target/action pair. If there is no assigned target/action pair, it gives the same failure that would happen if the user had not implemented an optional method in your protocol. The key here is to note that you can treat your standard delegate pointer exactly the same way you could before, including calls to

respondsToSelector:

to check for the implementation (or target/action registration) of optional methods.

Case 2: They use a standard delegate technique. Nothing tricky about this case – only difference is that your object has an extra DefaultDelegate object hanging around, which is not used any longer, and disappears when your object is deallocated.

The only drawback of the whole thing is that target/action callbacks have some extra overhead, since we need to look up the actual callback in a table at runtime per call. This cost is extremely small, but it would be bad in time-critical code, such as a call that often happens more than, say, 20 times per second on current iPhone hardware.

Source

<a href="http://bynomial.com/blogfiles/DefaultDelegate.h">DefaultDelegate.h</a>
<a href="http://bynomial.com/blogfiles/DefaultDelegate.m">DefaultDelegate.m</a>

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*