How Best to Use Delegates and Notifications in Objective-C
http://www.informit.com/articles/article.aspx?p=2091958
The question of notifications versus delegates is a hotly debated topic in Objective-C, and often developers fall into one of the two camps. By reading this article, you will hopefully understand scenarios where each makes sense.
Whether you're a newcomer to Objective-C or an advanced developer, you will no doubt encounter both notifications and delegates every day. Both of these are design patterns used extensively throughout Objective-C development. They are commonplace within system frameworks such as Cocoa and Cocoa Touch.
At first glance these two distinct patterns seem to offer similar, if not the same, functionality. However, for your code to be both efficient and easy to maintain, you should employ them in different scenarios. Understanding this can save you hours of time when you're trying to untangle the code you wrote months earlier.
What are Notifications & Delegates
Notifications
Notifications provide a means of broadcasting a message from anywhere to anywhere. The NSNotification class provides this functionality in Objective-C. It is not strictly a part of the language, but rather the Foundation framework. However, you'll rarely use Objective-C without having access to Foundation. Instances of NSNotification are broadcast through an NSNotificationCenter.
Notifications contain a name, an object and a dictionary of metadata. The object and metadata are optional, but the name is required. An object can register with the notification center to receive certain notifications, filtered by name, object or both name and object. Additionally, a selector is passed, which is called when a notification matching the filter is broadcast.
An example of a notification is as follows:
// Registering as observer from one object
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(thingHappened:)
name:MyThingHappened
object:_myObject];
// Posting notification from another object
[[NSNotificationCenter defaultCenter] postNotificationName:MyThingHappened
object:self
userInfo:nil];
In this example one object is registering itself as an observer for the MyThingHappened notification, limiting it to when that notification occurs on the exact object, _myObject. Then, another object is posting that notification with itself as the object and nothing as the metadata dictionary, userInfo. So in this case, if the object that's posting the notification is _myObject from the context of the registering object, then the notification will cause thingHappened: to be called.
Delegates
A delegate defines an interface through which interaction between two objects can occur. In Objective-C, this is usually achieved through the use of a formal protocol using the @protocol syntax. In a delegate scenario, one object is the delegate and one the delegator. The delegator will have a reference to its delegate, through which it sends messages by calling methods defined in the delegate protocol. An example of this would be a button in a user interface which might have a delegate it notifies when it has been clicked.
The simplest example of a delegate that you'll use almost every day as an iOS developer is UITableViewDelegate. If you look up the definition for this, it defines a large number of methods, but cutting it down to just one looks like this:
@protocol UITableViewDelegate <NSObject, UIScrollViewDelegate>
@optional
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@end
The table view itself has a reference to its delegate, declared as a property:
@interface UITableView : UIScrollView
@property (nonatomic, assign) id <UITableViewDelegate> delegate;
@end
NOTE
A delegate property is always declared weak (notice the assign attribute). This is to break any potential retain cycle introduced. Consider an object that is the delegate of a table view. It will likely hold a strong reference to the table view because it owns the table view and might want to manipulate the table view. If the delegate property was strong also, then a retain cycle would exist.
This provides a way in which the table view can tell its delegate when a row has been selected, something which is likely to be interesting. The table view has a property on it that holds a reference to its delegate. When a row is selected, the table view checks to see if it has a delegate and that the delegate responds to the tableView:didSelectRowAtIndexPath: selector. If these conditions are met, then the selector is invoked. The table view must check if the delegate responds to the selector because that delegate method is marked as "optional."
TIP
My book, Effective Objective-C 2.0, goes into more depth about the delegate pattern and some improvements you can use to make it more efficient.
Comparing & Contrasting
I don't believe there is a right and wrong answer to the question, "notification or delegate?" Sometimes it's right to use one, sometimes it's right to use the other. Below I compare and contrast various features of each and explain scenarios where each should be used.
Coupling
Notifications result in loose coupling between objects. The coupling is loose because the object sending a notification doesn't know what is listening to the notification. This loose coupling can be extremely powerful because multiple objects can all register to listen to the same notification, from different parts of an application. However, loose coupling can also make the use of notifications a nightmare to debug, since code can easily become entangled. Following the code paths produced when a notification is broadcast can become a daunting experience.
On the other hand, delegates result in tight coupling between objects. This is because the delegating object has a reference directly to its delegate. It knows if it has one and can introspect to find which delegate methods it implements, if any. The selectors that are called are defined in the protocol rather than notifications which can use any selector.
The fact that notifications and delegates provide such different coupling are an indicator that they should be used in different scenarios. If table view used notifications instead of a delegate, then all classes that use a table view could choose different method names for each notification. This would make it hard to understand code as you would need to go and find the notification registration to work out which method is called. With a delegate, it's obvious: all classes that use a table view are enforced to be structured in the same manner.
Therefore, use a delegate when it makes sense to enforce the selector that is being called. This would be scenarios where the structure of code in consumers of the delegate would benefit from being enforced. Within Cocoa development, this pattern works well for the view to view controller part of the MVC (Model-View-Controller) paradigm.
On the other hand, use a notification when you definitely don't want to couple the two sides of the notification. An example of where this would be desirable is login handling within an application. You don't want to couple all the various parts of an application that want to know about login state to the login handler.
Object Ownership
Consider your object model as a tree. An object should have just one owner, but can own multiple objects. Delegation is a way to pass a message up the tree to its owner. On the other hand, notifications are a way to message between completely separate parts of the object tree, where it would be extremely clunky to pass messages all the way up the tree to be distributed back down.
A delegate should only be used when the object being the delegate owns the object doing the delegation. A view controller owns a table view and is the delegate of that table view. For this reason you should avoid having a delegate on a singleton. It's almost never the right solution. A singleton doesn't really have an owner, or you can consider itself or the application to be its owner. One object might set itself as the delegate of a singleton, but then another might come along and break that relationship by setting itself as the singleton's delegate. The original delegate wouldn't know this had happened and would still think it was the delegate. Therefore if you're working with a singleton, avoid adding a delegate and opt for notifications instead.
NOTE
Apple sometimes uses delegates with singletons. Examples are UIApplication and NSFileManager. These are special cases though. It's unusual to set the delegate on UIApplication other than when it is instantiated, so that doesn't suffer from the same problem of breaking the relationship from underneath the original delegate. As for NSFileManager, it is not only used as a singleton. It's possible to create other file managers besides the default, singleton manager. Setting a delegate on the default manager would be confusing, but not on other managers.
Multiple Listeners
By design, notifications offer multiple listeners. There can be zero, one or many listeners for any one notification. If you are certain that you'll need multiple listeners, a notification is the correct route to go down. The instance above of login state handling is a perfect example of this. It's likely that multiple objects are going to care about login state changing, so a notification makes sense.
The standard delegate pattern allows only one delegate to be defined. However, it would be possible to provide a multiple delegation scheme by holding a set of delegates rather than just one reference. However, this is usually a bad idea as care has to be taken to ensure that retain cycles are broken. Collections in Objective-C hold strong references to their contents by default. I discourage you from trying this, though, because it is almost always the wrong pattern to use. As soon as multiple listeners are required, either you should be using notifications or you have structured your code incorrectly.
Thinking about object ownership, multiple delegates would imply multiple owners, which often leads to retain cycle issues or difficult-to-understand code.
Is the Object Known?
Sometimes you don't actually know the precise object you want to communicate with. An example of such a scenario is the keyboard in iOS, which is a view which is handled entirely by iOS for you. The keyboard comes up as required and obscures your UI. Knowing when the keyboard is being presented and when it is being hidden is therefore a common thing to care about. However, the keyboard view is entirely handled by iOS and should not be exposed, because doing so would mean apps could manipulate it, something which Apple clearly doesn't want people to do. Also, it's likely that multiple objects will care about the keyboard showing & hiding. Most view controllers will care so that they can reflow their layout to accommodate the loss in screen real estate. This is a prime example of using notifications. These are the notifications that UIKit provide for keyboard showing and hiding:
NSString *const UIKeyboardWillShowNotification;
NSString *const UIKeyboardDidShowNotification;
NSString *const UIKeyboardWillHideNotification;
NSString *const UIKeyboardDidHideNotification;
These notifications illustrate an important difference between delegates and notifications. Delegate methods describe exactly the information that is communicated by defining the parameters to the method. Notifications on the other hand need to use the metadata dictionary, called userInfo, to pass information. This makes them slightly clunkier to use. The keyboard notifications described above make use of this metadata dictionary to provide information such as the frames of the keyboard. However, how do you know what key each bit of information is stored as within the metadata dictionary? You need to define the keys. UIKit does this for the keyboard notifications like so:
NSString *const UIKeyboardFrameBeginUserInfoKey;
NSString *const UIKeyboardFrameEndUserInfoKey;
This adds complexity on top of a rather simple concept. In the case of keyboard notifications, this is a small price to pay for the benefit of notifications (multicast and not needing to know the precise keyboard view). However you should be sure to keep in mind this additional complexity when deciding between notifications and delegates.
Can I Use Both?
As you start to look around Apple's system frameworks, you'll often see both notifications and delegates being used for the same messages. A prime example of this is UIApplication which is a singleton holding the state of the running application. It has a delegate, called UIApplicationDelegate, which defines methods that are called as things happen such as the application going into or out of the background. However UIKit also fires notifications when each of these things happen. This is extremely useful because it's common for various parts of an application to care about the application state. A download may need to be paused when going into the background, or audio stopped playing, for example. If the application had to proxy all this through the application delegate then the code of the application delegate would become very long and complicated.
You might suggest at this point why bother having the delegate at all. The reason for having the delegate in the first place is that UIApplication needs to employ a certain feature of delegates that notifications cannot provide. This is the ability for an object to ask its delegate for information, which is passed back as the return value of a delegate method. The following UIApplicationDelegate method uses this:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
This method is called on the delegate when the application is being requested to open a certain URL. It's the delegate's job to handle that URL if it can. If it can, it should return YES; otherwise it should return NO. The application then knows whether or not the URL has been handled. Internally, UIApplication uses this information to know whether it should try to handle it itself or not.
So in the case of UIApplication, a delegate is required anyway, therefore it makes sense to provide the flexibility to the developer of using either the notification or the delegate. Some applications will only ever use the delegate, some will use both the delegate and the notifications. You may choose to offer the same flexibility in your own design.
Summary
The question of notification versus delegate is a hotly debated topic and often developers fall into one of the two camps. I encourage you to believe in the mantra of "horses for courses." Use each where each makes sense. Hopefully, by reading this article, you understand scenarios where each has its own benefits. Now go and write more effective Objective-C!