The Motivation
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 NSAnimationDelegate protocol. 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.
The Problem
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).
The Solution
This is how the Observable class looks like:@interface Observable : NSObject - (void)addObserver:(id<NSObject>)observer; - (void)removeObserver:(id<NSObject>)observer; - (void)notifyObservers:(NSInvocation*)invocation; @endIt 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. Since this is Objective-C (possibly for iOS, e.g. iPhone), we need to be careful not to retain the observers to avoid circular references. So we need to create a non-retaining mutable set:
observers = (NSMutableSet*)CFSetCreateMutable(NULL, 0, NULL);The reason it is a set 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]; [pendingAdds addObject:observer]; } else { [observers addObject:observer]; } }The code for removeObserver: is very similar and the code for commitPending is straight forward.
Conclusion
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; @endHere 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]; } @endNotice 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 Wikipedia entry or the GOF book. 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. To compile you will need Google Toolbox for Mac.
출처 : http://www.a-coding.com/2010/10/observer-pattern-in-objective-c.html