The observer pattern is useful whenever you want a dynamic list of objects (observers) be notified when an event occurs in an observed object (also called the subject).
Cocoa provides several built-in ways to make use of this pattern. If you are interested in a profound comparison, Matt Gallagher has a great summary of the standard methods for observation and notification in Cocoa.
In this post I am going to contribute an implementation of the observer pattern that resembles the way delegates are implemented in Cocoa. In contrast to Cocoa built-in mechanisms (like NSNotificationCenter) the presented approach enables you to use arbitrary selectors in notification messages (just as is the case with delegates). What is more you won’t have to cope with the details how notification messages are dispatched by the broadcaster.
Note: for what it’s worth, I will call the broadcaster “observable” and the listener “observer” throughout this post. This is in line with the classes I’m providing in my sample code.
Basic Idea
We would like to be able to notify multiple observers about a certain event, giving them a chance to act as appropriate. Just like with delegates, this should be possible using arbitrary selectors.
Let’s take a look at a standard delegation example in Objective-C:
- (void)performTask
{
// Check whether delegate implements -willPerformTask
if ([delegate respondsToSelector: @selector(willPerformTask)]) {
// Notify delegate that we're about to perform the task
[delegate willPerformTask];
}
}
The example above, of course, is limited to one delegate receiving notification messages from the delegator. Now, what we want in certain situations is a collection of observers (multiple “delegates”). All observers should receive the notification message.
It would be neat if the code in your observable object would look something like this:
- (void)performTask
{
// Notify observers that we're about to perform the task
[self.observers willPerformTask];
}
The idea here is that observers acts as a proxy broadcasting the willPerformTask message to all observer objects implementing the message’s selector.
This is exactly what the Observers class that I provide in the Observer sample code will do for you.
A Closer Look
Let’s take a closer look at what we’re talking about here.
The Observer sample provides two classes responsible for implementing observer registration and message broadcasting:
Observable: Abstract base class giving subclasses the ability to easily adopt the role of “generic” broadcasters.Observablemaintains anObserversobject and allows other objects to add and remove observers.Observers: This class implements the actual broadcasting mechanism for Objective-C messages to arbitrary observer objects (must be Objective-C objects.) The basic idea here is that all messages sent toObserversare automatically broadcasted to all observers responding to a given selector (implementing/listing to a given notification message.)
An Example
For what follows, let’s assume the following example: we’d like to implement a simple contact manager that is capable of managing a collection of contact names. The contact manager provides two methods, -addContactWithName: and -removeContactWithName:. What is more, our contact manager should be observable, i.e. an arbitrary number of observers should be notified whenever a contact is being added or removed.
Implementing an Observable Contact Manager
Our contact manager is defined in the ContactManager class which inherits from Observable so as to provide for observation capabilities (see ContactManager.h in the sample code):
#import "Observable.h"
@interface ContactManager : Observable {
NSMutableArray *contactNames;
}
- (void)addContactWithName:(NSString *)name;
- (void)removeContactWithName:(NSString *)name;
@end
By inheriting from Observable, ContactManager is equipped with an Observers object which is capable of maintaining a collection of observers and broadcasting messages to them.
As a consequence, within ContactManager’s implementation of -addContactWithName: we can do the following (see ContactManager.m in the sample code):
- (void)addContactWithName:(NSString *)name
{
[contactNames addObject:[name copy]];
[self.observers contactManager:self
didAddContactWithName:[name copy]];
}
-removeContactWithName: is implemented likewise:
- (void)removeContactWithName:(NSString *)name
{
[contactNames removeObjectIdenticalTo:name];
[self.observers contactManager:self
willRemoveContactWithName:[name copy]];
}
Note: Observable provides two methods to add and remove observers:
-(void)addObserver:(id)observer;and-(void)removeObserver:(id)observer;.
Since ContactManager is a Observable subclass, we can later wire observers to the contact manager by using these methods.
Implementing a Contact Manager Observer
The observer may be defined as a simple NSObject subclass providing implementations for the notification selectors it is interested in. Consider the following example (see ContactObserver.h and ContactObserver.m in the sample code):
@interface ContactObserver : NSObject
@end
@implementation ContactObserver
- (void)contactManager:(ContactManager *)manager
didAddContactWithName:(NSString *)name
{
NSLog(@"Observer got notification: contact manager added contact with name %@", name);
}
- (void)contactManager:(ContactManager*)manager
willRemoveContactWithName:(NSString *)name
{
NSLog(@"Observer got notification: contact manager is about to remove contact with name %@", name);
}
@end
Wiring Observers with Observables
Finally, what we have to do to kick things off is wire the observer with the observable. In our example this looks as follows (see main.m in the sample code):
ContactManager *manager = [[ContactManager alloc] init]; ContactObserver *observer = [[ContactObserver alloc] init]; [manager addObserver:observer]; // [...]
Providing a Syntactically Correct Implementation with Protocols
While the code presented above will work, it is likely to cause compiler warnings due to the fact that observers are of type id. The compiler cannot know for sure that observers are going to respond to messages like contactManager:didAddContactWithName:, because id does not implement these methods.
The best solution here is to provide a protocol for each type of observer. This is not strictly necessary, but it tidies up your code and supports a good understanding of how things are structured.
It’s a good idea to add the protocol to the Observable’s header and name it accordingly.
In ContactManager.h, we add:
@protocol ContactManagerObserver @optional - (void)contactManager:(ContactManager *)manager didAddContactWithName:(NSString *)name; - (void)contactManager:(ContactManager *)manager willRemoveContactWithName:(NSString *)name; @end
Consequently, the observer class should implement the newly defined protocol.
In ContactObserver.h we then have:
#import "ContactManager.h" @interface ContactObserver : NSObject <ContactManagerObserver> - (void)contactManager:(ContactManager *)manager didAddContactWithName:(NSString *)name; - (void)contactManager:(ContactManager *)manager willRemoveContactWithName:(NSString *)name; @end
Finally, we need to change how ContactManager sends notification messages to observers.
In ContactManager.m, consider the “add contact” example employing the protocol as follows:
- (void)addContactWithName:(NSString *)name
{
[contactNames addObject:[name copy]];
[(id<ContactManagerObserver>)self.observers contactManager:self
didAddContactWithName:[name copy]];
}
Viola, by telling the compiler that observers conform to the ContactManagerObserver protocol, there’s nothing to complain about anymore.
Characteristics of the Presented Observer Mechanism
There are some key characteristics of what you can do with observers when implementing them in the way discussed here:
- Arbitrary method signatures: you may use selectors with arbitrary signatures for notification messages.
- No obligation to handle all observer messages: it is not necessary to implement all methods for handling messages sent by the observable object. That is, in our example an observer may implement
-contactManager:didAddContactWithName:only, omitting-contactManager:didRemoveContactWithName:. - Order of notification message dispatch undefined: you should keep in mind that the order in which notifications are sent to observers is not defined with this approach. As a consequence, you should avoid making assumptions on what other observers do in response to an observable notification.
- Weak references to observers: the
Observersclass holds weak references to observer objects to avoid retain cycles. Consequently, you must take care of the observer vs. observable lifecycle on your own.
Limitations
There are some limitations with the presented approach:
- Observer method implementations cannot return values: due to the nature of the broadcaster mechanism employed here, observer methods must exhibit a
voidreturn type. - Sample code is not optimized for performance: you should not use this approach for performance critical operations. The
Observersclass implements broadcasting in a fairly naive way, asking all registered observer objects whether they respond to the given selector. For large numbers of observer, this may represent a major performance hit. You should consider using another method in such cases.
Behind the Scenes: How Does Message Forwarding Work?
There is a plethora of documentation on Objective-C message forwarding, and I am not going to re-iterate on that here. However, to give you a basic understanding of what the code in the Observers class does, I provide a brief explanation here. And also there’s one little thing to add that has not yet been discussed profoundly on the web yet—swallowing selectors with an unknown method signature—but more on that later.
Message Forwarding and How To Swallow Messages With Unknown Signature
At the heart of the Observers message forwarding mechanism, there are two methods: methodSignatureForSelector: and forwardInvocation:.
These methods are called by the Objective-C runtime whenever a selector is sent to an object which is not implemented inside the compiled code of the receiver’s class.
Essentially, the runtime first calls methodSignatureForSelector:. If it gets back a valid method signature, it continues with calling forwardInvocation:. This gives custom code a chance to implement message forwarding for messages not implemented directly in the receiver’s class.
Be wary, that the runtime will cause an exception (unrecognized selector sent to instance) when methodSignatureForSelector: returns nil.
Let’s have a look at Observer implementation of methodSignatureForSelector: first:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if(!signature) {
for(id observer in observers) {
if([observer respondsToSelector:aSelector])
return [observer methodSignatureForSelector:aSelector];
}
signature = [NSMethodSignature signatureWithObjCTypes: "@^v^c"];
}
return signature;
}
This method is called by the Objective-C runtime to request a method signature for a given selector. Observers implements this method so as to ask each registered observer whether it is capable of responding to the given selector. If so, the method signature is requested from the first observer exhibiting that capability.
If no observer capable of responding is found, we have to quietly bailout—without causing an exception.
This is exactly what the following line does:
signature = [NSMethodSignature signatureWithObjCTypes: "@^v^c"];
Recall that observables should be able to simply send an arbitrary observer selector to their observers. This means we may run into a situation where no observer implements the given selector. Usually this would cause the runtime to throw an exception, because an unrecognized selector is sent to an instance.
The key to resolving this situation without running into runtime errors is to provide an arbitrary method signature. That is, we just simply ignore that we have no clue about the selector’s designated method signature by returning a default signature. This is possible, because we just want to swallow the invocation later on, i.e. there is no invocation in response to the message being sent to the observers instance.
Finally, this is what the corresponding forwardInvocation: implementation looks like:
- (void)forwardInvocation:(NSInvocation*)anInvocation
{
for(id observer in observers) {
if([observer respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:observer];
}
}
}
This code checks whether there is a registered observer that responds to the given invocation’s selector. If there is one, we can be sure that methodSignatureForSelector: did successfully respond with the correct method signature for the required selector. If not, we simply do nothing. Thus, it is completely irrelevant whether the previously requested method signature was correct or not.
I found one single blog post about Elegant Delegation that explicitly discusses this issue. There is also a post by Matt Gallagher about collecting arbitrary messages using NSInvocation. While that post is great, the method provided there appears overly complex for the problem at hand in this case.
Sample Code
Download Observer Sample Code (22 KB)





3 responses so far ↓
1 A Delegate-like Observer Mechanism in Objective-C using Forward … | effort.ly Sep 23, 2011 at 9:25 am
[...] article: A Delegate-like Observer Mechanism in Objective-C using Forward … /* */ /* */ window.fbAsyncInit = function() { [...]
2 bob Feb 2, 2012 at 3:44 am
This is not true: “it is likely to cause compiler warnings due to the fact that observers are of type id”
The point of id is that it does not check whether a method is supported
You might get a separate compiler warning that the selectors were not declared — the compiler must know the signature of a selector in order to compile a call to it correctly. That’s why you implemented a protocol — simply to provide a declaration to the compiler. You do not actually need to implement this protocol.
3 Tobias Feb 21, 2012 at 2:57 pm
You are right. Bad wording. I’m gonna fix this.
Leave a Comment