ObjC Delegation and Notifications

I was researching the different ways people are implementing the part of MVC where the model notifies the controller about new data. A limitation of the delegate pattern is only one class can be the delegate, for other classes to listen then they need to use Notification Center notifications (or use a custom multi-cast delegate). Normally when the model class is called to say, insert an object, it would use the standard routine to check the delegate responds to the selector, and then call the delegate method as defined on the protocol. Then when reading some Apple’s delegations article I noticed something rather interesting:

Delegation and Notifications
The delegate of most Cocoa framework classes is automatically registered as an observer of notifications posted by the delegating object. The delegate need only implement a notification method declared by the framework class to receive a particular notification message. Following the example above, a window object posts an NSWindowWillCloseNotification to observers but sends a windowShouldClose: message to its delegate.

The interesting point here is that the delegate is registered as an observer, which is different from calling the delegate method directly. To check this was a correct statement I went to the gold mine that is the GNUStep source code for NSWindow (Github Mirror) and found this very nifty piece of code:

- (void) setDelegate: (id)anObject
{
  if (_delegate)
    {
      [nc removeObserver: _delegate name: nil object: self];
    }
  _delegate = anObject;
#define SET_DELEGATE_NOTIFICATION(notif_name) \
if ([_delegate respondsToSelector: @selector(window##notif_name:)]) \
[nc addObserver: _delegate \
selector: @selector(window##notif_name:) \
name: NSWindow##notif_name##Notification object: self]

SET_DELEGATE_NOTIFICATION(DidBecomeKey);
SET_DELEGATE_NOTIFICATION(DidBecomeMain);
SET_DELEGATE_NOTIFICATION(DidChangeScreen);
SET_DELEGATE_NOTIFICATION(DidDeminiaturize);
SET_DELEGATE_NOTIFICATION(DidExpose);
SET_DELEGATE_NOTIFICATION(DidMiniaturize);
SET_DELEGATE_NOTIFICATION(DidMove);
SET_DELEGATE_NOTIFICATION(DidResignKey);
SET_DELEGATE_NOTIFICATION(DidResignMain);
SET_DELEGATE_NOTIFICATION(DidResize);
SET_DELEGATE_NOTIFICATION(DidUpdate);
SET_DELEGATE_NOTIFICATION(WillClose);
SET_DELEGATE_NOTIFICATION(WillMiniaturize);
SET_DELEGATE_NOTIFICATION(WillMove);
}

So sure enough, it adds the delegate as an observer, and only doing so if it responds to the selector. It’s is a pretty good optimisation because now at each event only the notification needs to be posted. Previously, the delegate performing the selector would be checked every time (still done this way for delegate methods that contain data). I also really like the macro. The only problem is the notifications are rather primitive in that they contain no data about the reason, they only contain the object self. In seeing this implementation it sparked my interest to investigate if Apple implemented it the same way, which could be tested by removing the delegate as an observer and then seeing if the delegate method is no longer called, don’t think I’ll bother just now though.

Leave a Reply

Your email address will not be published. Required fields are marked *