Notifications
Cocoa provides your app with a single instance of NSNotificationCenter, informally called the notification center. This instance is the basis of a mechanism for sending messages called notifications. A notification includes an instance of NSNotification (a notification object). The idea is that any object can be registered with the notification center to receive certain notifications. Another object can hand the notification center a notification object to send out (this is called posting the notification). The notification center will then send that notification object, in a notification, to all objects that are registered to receive it.
An NSNotification object has three pieces of information associated with it, which can be retrieved by instance methods:
1.its name, an NSString which identifies it;
2.An object associated with the notification (typically the object that posted it);
3.And its userInfo.Not every notification has a userInfo; it is an NSDictionary, and can contain additional information associated with the notification.
What information this NSDictionary will contain, and under what keys, depends on the particular notification; you have to consult the documentation. For example, the documentation tells us that UIApplication’s UIApplicationDidChangeStatusBarFrameNotification includes a userInfo dictionary with a key UIApplicationStatusBarFrameUserInfoKey whose value is the status bar’s frame. When you post a notification yourself, you can put anything you like into the userInfo for the notification’s recipient(s) to retrieve.
Receiving a Built-In Notification
Cocoa itself posts notifications through the notification center, and your code can register to receive them. You’ll find a separate Notifications section in the documentation for a class that provides them.
To register for a notification, you use the addObserver:... instance method. The instance to which it is sent will typically be the app’s single default notification center, [NSNotificationCenter defaultCenter]. The parameters are as follows:
addObserver:
The instance to which the notification is to be sent. This will typically be self; it isn’t usual for one instance to register a different instance as the receiver of a notification.
selector:
The message to be sent to the observer instance when the notification occurs. The designated method should return void and should take one parameter, which will be the NSNotification object (so the parameter should be typed as NSNotification* or id).
name:
The NSString name of the notification you’d like to receive. If this parameter is nil, you’re asking to receive all notifications sent by the object designated in the object parameter. A built-in Cocoa notification’s name is usually a constant. This is helpful, because if you flub the name of a constant, the compiler will complain, whereas if you enter the name of the notification directly as an NSString literal and you get it wrong, the compiler won’t complain but you will mysteriously fail to get any notifications (because no notification has the name you actually entered) — a very difficult sort of mistake to track down.
object:
The object of the notification you’re interested in, which will usually be the object that posted it. If this is nil, you’re asking to receive all notifications with the name designated in the name parameter. If both the name and object parameters are nil, you’re asking to receive all notifications.
For example, in one of my apps I need to respond, by changing my interface, if the user starts or stops playing a song from the device’s music library. The API for the device’s built-in music player is the MPMusicPlayerController class; it provides a notification to tell me when the built-in music player changes its playing state, listed under Notifications in the MPMusicPlayerController’s class documentation as MPMusicPlayerControllerPlaybackStateDidChangeNotification.
It turns out, looking at the documentation, that this notification won’t be posted at all unless I call MPMusicPlayerController’s beginGeneratingPlaybackNotifications instance method. This architecture is not uncommon; Cocoa saves itself some time and effort by not sending out certain notifications unless they are switched on, as it were. So my first job is to get an instance of MPMusicPlayerController and call this method:
MPMusicPlayerController* mp = [MPMusicPlayerController iPodMusicPlayer];
[mp beginGeneratingPlaybackNotifications];
Now I register myself to receive the desired playback notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playChanged:)
name:MPMusicPlayerControllerPlaybackStateDidChangeNotification
object:nil];
So now, whenever an MPMusicPlayerControllerPlaybackStateDidChangeNotification is posted, my playChanged: method will be called:
- (void)playChanged:(id) n {
// ... do something in response ...
}
Unregistering
It is up to you, for every object that you register as a recipient of notifications, to unregister that object before it goes out of existence. If you fail to do this, and if the object does go out of existence, and if a notification for which that object is registered is posted, the notification center will attempt to send the appropriate message to that object, which is now missing in action. The result will be a crash at best, and chaos at worst.
To unregister an object as a recipient of notifications, send the notification center the removeObserver: message, whose parameter is the object that is no longer to receive notifications. (Alternatively, you can unregister an object for just a specific set of notifications with removeObserver:name:object:.) The trick is finding the right moment to do this. In most cases, the easiest solution is the registered instance’s dealloc method, this being the last event an instance is sent before it goes out of existence.