I've always wondered why the Foundation and Cocoa frameworks in Objective-C are so biased towards the delegate pattern. Don't get me wrong, I don't have anything against it. But there are some cases where using the observer pattern would make more sense. Take, for instance, the NSAnimationDelegateprotocol. Most of the 'delegate' methods are actually just state change notifications. Out of the five methods in this protocol only one is an actual delegate method: animation:valueForProgress:.

The absence of the observer pattern, especially in the UI classes, prompted me write a reusable observable class to use in my custom UI code. Similar to Java's Observable class.

Probably the biggest implementation difference between the observer pattern and the delegate pattern is that observable objects support multiple observers, while the delegate is just one object. But this is precisely what makes the observer pattern one of my favorites. It is also what makes it harder to implement.

Also, doing a bullet-proof implementation requires handling the case of an observer being added or removed inside a notification call. The observer collection cannot mutate while iterating so it needs a clever way of handling this (for some ways of doing this look here).

This is how the Observable class looks like:

@interface Observable : NSObject
- (void)removeObserver:(id<NSObject>)observer;
- (void)notifyObservers:(NSInvocation*)invocation;
@end


It is pretty standard, except that you pass an NSInvocation when you want to send a notification. This is so that we have flexibility with observer protocols. If you haven't, you might want to check out the previous post: Making NSInvocations, but more on this later.

We start by creating the observer collection. We need to be careful not to retain the observers to avoid circular references. For this we use an NSHashTable of weak references:

observers = [NSHashTable weakObjectsHashTable];


The reason it is a hash table and not an array is to make adding and removing observers faster.

And here is how you notify the observers:

- (void)notifyObservers:(NSInvocation*)invocation {
notifying = YES;
for (id<NSObject> observer in observers) {
if (![pendingRemoves containsObject:observer] &&
[observer respondsToSelector:[invocation selector]]) {
[invocation setTarget:observer];
[invocation invoke];
}
}
notifying = NO;
[self commitPending];
}


The notifying flag and the commitPending method call are there to handle addition and removal of observers inside a notification. If the notifying flag is set then we don't add the observer to the main observers collection. We instead add it to a temporary collection (pendingAdds) and only in commitPending do we actually add it to the main observers collection. Here is the code:

- (void)addObserver:(id<NSObject>)observer {
if (notifying) {
[pendingRemoves removeObject:observer];
} else {
}
}


The code for removeObserver: is very similar and the code for commitPending is straight forward.

Let me finish by showing how you would use this. Let's say you have an animation class you want to make observable. You want to receive notification for the animation starting or stopping:

@protocol AnimationObserver
- (void)animationDidStart;
- (void)animationDidStop;
@end


Here is how you would implement the Animation class:

@interface Animation : Observable
...
- (void)start;
- (void)stop;
@end

@implementation Animation
...
- (void)start {
...
NSInvocation* inv = [NSInvocation
invocationWithProtocol:@protocol(AnimationObserver)
selector:@selector(animationDidStart)]
[self notifyObservers:inv];
}

- (void)stop {
...
NSInvocation* inv = [NSInvocation
invocationWithProtocol:@protocol(AnimationObserver)
selector:@selector(animationDidStop)]
[self notifyObservers:inv];
}
@end


Notice how it uses the invocationWithProtocol:selector: method from the previous post, Making NSInvocations. Actually this is precisely the use case I had in mind when I implemented the NSInvocation additions.

So there you have it. For more information on the observer pattern you can check out the GOF book or the Wikipedia entry. If you have questions or further improvements feel free to leave a comment.

The source code for this article, including unit tests, is available at GitHub. You can also add this to your project via CocoaPods by adding pod 'AIObservable' to your Podfile.