I have moved my blog to Wordpress at theunixgeek.wordpress.com. I will still be checking back periodically on this one as well, though. 19 April 2009


Merging Mkdir and Cd | 280 Slides Interview | I Switched to KDE 4

clickable portals

Monday, January 12, 2009

Understanding Cocoa Notifications

Thanks to Martin Pilkington and Steven Degutis for helping out with this article!

Being a Cocoa developer, you are probably aware of delegates. Delegates are "helper objects" that run custom code when called by its object. For example, the class NSWindow has the delegate method windowDidMove:. If I have a Controller object with that method implemented and I set it to be a certain NSWindow's delegate via [myWindow setDelegate:aController]; then whenever the window moves, my delegate object will be notified of it and run the code in the windowDidMove: method's implementation.

The problem with delegates, however, is that an object can only have one delegate object, so it can't notify multiple objects of something at once via delegation. Also, you can't get feedback from all instances of a class, while with notifications, you can call a method when any window in your app is moved, minimized, closed, etc. That is the great part of a notification: notifications allow the notifier to notify multiple objects at once.

The two main classes used in notifications are NSNotification and NSNotificationCenter. NSNotification is very simple: it contains a name for the notification, the object that sent the notification, and an optional userInfo dictionary. But where the real magic lies is in NSNotificationCenter. With it you can basically do three things: add an observer, remove an observer, and post a notification. 

Code Dissection: Adding an Observer
Let's continue using the window moving idea. Take a look at the following piece of code:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(aWindowMoved:) name:NSWindowDidMoveNotification object:nil];

  • [NSNotificationCenter defaultCenter] is a shared instance.
  • addObserver:self sets the current class to be the observer of defaultCenter. This is useful in a controller class, but self can be replaced to any other object that contains the method referred to in the next argument.
  • selector:@selector(aWindowMoved:)  is the name of the method within the observer that should be called when the notification (in the next argument) is sent out. Selector declarations should have something similar to (NSNotification *)aNotification as an argument, so aWindowMoved: is defined as -(void)aWindowMoved:(NSNotification *)aNotification; in the observer's @implementation.
  • name:NSWindowDidMoveNotification is the name of the notification. You can find these in the documentation for objects that have support for notifications
  • object:nil says that any object that has an NSWindowDidMoveNotification notification can notify the observer. If you wanted to make this more delegate-like, though, you could specify an object, such as object:myWindow
Code Dissection: Removing an Observer
Removing observers is just as simple. The simplest way to remove an observer is like so:

[[NSNotificationCenter defaultCenter] removeObserver:self];

That should be pretty much self-explanatory. However, if you want to remove one observance and keep the rest intact, use the -removeObserver:name:object: method. Here are a few examples:

  • [[NSNotificationCenter defaultCenter[ removeObserver:self name:NSWindowDidMoveNotification object:nil]; removes self as the observer for all move notifications.
  • [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:myWindow]; removes self as the observer from move notifications from myWindow specifically.
  • [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:myWindow]; removes self as the observer from all notifications from myWindow
Other possibilities exist as well.

Try it out!
Write an application with a window and a panel. The window should have a check box saying "warn me when this window moves." When the window moves, have the panel appear with a message on it: "a window moved" if the check box is checked. 

Hint 1: Check Boxes are a type of NSButton. 
Hint 2: NSButtons need neither delegates nor notifications. Being derived from NSControl, they only use targets and actions.

Click here to view the answer to the exercise.