In this post, I’ll enumerate four major types of callback mechanisms in Objective-C, and mention some comparisons to help choose the best one for whichever class you’re designing.
Intuitively, a callback is a method that you think of as being called from outside your code.
More formally, a callback is a method that can be called by other classes which may be created after, or independently of, your class. These methods are named callbacks because we have to explicitly provide a pointer to the caller so they can “call us back” later. Two classic examples: an asynchronous NSURLConnection needs a way to report back when it’s done loading a page, and a UIButton needs a way to report that a user pressed the button.
Types of Callbacks
Delegation or Formal Protocols
Delegation is one of the most common callback mechanisms in current Objective-C code. The use of formal protocols (or just “protocols” in the language spec) is almost synonymous with delegates, so I’ll group them together here; although you can use protocols for non-delegate purposes, too.
To use this method, you declare a set of methods in a @protocol block, and require the class getting called back to be set as your delegate object – generally this is a weak reference (not retained) to an id<MyClassDelegate> type. When you want to call the callback, you can simple execute a [delegate doMyCallbackWithObject:x] statement on required methods. For optional methods, first check that they’re implemented, and either call them or execute your default behavior.
UITableViewDelegate is a good example protocol for this callback mechanism.
UIControl and therefore its subclasses – including UIButton, UISwitch, and UISlider, to name a few – all use a target/action pair method to handle callbacks.
To use this method, the class getting called back must specify both a selector (the action) and an object pointer (the target) that you can call at any time. It is up to the target to make sure the signature (i.e. the set of input and output parameters) of the action is something useful to your class. Notice that each selector has to be registered separately, as opposed to the delegate method which registers all the callbacks in one set.
Informal Protocols or Category Callbacks
Objective-C didn’t always have the @optional keyword for @protocols, which makes delegation very practical. Before @optional, it was common to add a category to NSObject which implemented the callbacks you needed – this is an informal protocol. This is similar to a formal callback in that all the names and signatures of the methods are fixed and available in a header file, but it differs in that all objects necessarily have a default implementation (no required or optional distinction) and a class can’t officially declare that it overrides these methods (but we can still find out at runtime of course).
Apple is phasing out informal protocols in favor of formal ones, although they can still be useful in situations where you’d prefer all objects to have easy access to default implementation of the protocol.
NSURLConnection is an example of an informal protocol callback mechanism.
Notifications are ideal for broadcasting messages (one-caller-to-many-callback-receivers) or for some decoupling of the caller and receiver.
To use this method, the caller simply posts an NSNotification object to an NSNotificationCenter; any classes that are interested in being called from this must have previously registered themselves as an observer of that notification. Notifications are identified by strings, and there is no formal way to declare which notifications your code produces – this communication must happen in comments/documentation (or by hand-parsing the code if you have to).
UIKeyboardWillShowNotification is an example notification name (it’s a string) that is posted when the keyboard is about to show up. This gives UI elements a chance to react by moving important views so they’re not obstructed by the keyboard.
Choosing a type of callback
Each approach has pros and cons. To help see this, consider the use cases for UITableViewDelegate (delegation), UIButton (target/action), NSURLConnection (informal protocol), and UIKeyboardWillShowNotification (notification).
Delegation Pros: One object pointer connects all the methods, signatures and names of methods are clearly documented, required and optional method distinction. Cons: Impractical to be a delegate for multiple objects, subclassing the called back class is challenging.
Target/Action Pros: Easy to work with multiple objects giving callbacks, flexible signatures, only one method at a time is required. Cons: Practical only for small sets of methods, signatures are not formally declared, receiver must unregister itself before it’s gone.
Informal Protocol Pros: Default implementation always available, possible to avoid specifying a target. Cons: No compile-time declaration of following an informal protocol, fills up the method namespace, no required/optional distinction.
Notification Pros: There can easily be many targets, caller doesn’t need any pointers to targets, receiver and target can be created in any order. Cons: Input parameters must be packaged in a userInfo dictionary, notification names are not formally declared, receiver must unregister itself before it’s gone, slightly longer calling time due to indirection.
How to choose?
If you might have many callers for the same target (such as a custom UI control), then target/action is probably good.
If you might have many targets for a single caller, then a notification is probably good, although you could also handle this with target/action if tighter coupling is preferred.
If you expect a one-to-one pairing between caller and target, then a delegate is probably the way to go.
Are there still times to use informal protocols? Some folks might say no, but I’ll argue against that.
A case for informal protocols
When would you like to use informal protocols? I suggest using them when you want to implement a simple asynchronous interface with optional callbacks for a universally usable piece of functionality.
For example, suppose you’re writing a serialization method that uses Objective-C’s reflection to convert any class into a string (your own version of NSCoding, basically). You’d like to allow any object to be able to call:
And have this work out. In this case, a category on NSObject is perfect. Now, suppose you want to give users a chance to react to an error asynchronously, but only if they want to. Then you could implement your own version of [NSObject writeToURLFailedWithError:(NSError *)error], which simply NSLogs the error, and allow users to override this method. This is nice because the user’s class never has to specify itself as a delegate or a target/action – you’re aware of both the target (since you have access to self in writeToURL:) and the action (since you name the selector in your category). In other words, we’ve just made using this functionality a little easier by using an informal protocol.
- Cocoa Fundamentals Guide: Object Communications – The best general documentation for all this stuff.
- A concise explanation of formal vs. informal protocols
- The Objective-C language spec for protocols
- The Target-Action Mechanism – part of the UIControl class reference
- Notification Programming Guide
- NSKeyValueObserving Protocol Reference – An example informal protocol, and can be used to essentially create yet another type of callback mechanism – although, that’s a subject for another post.