KVO+Blocks: Block Callbacks for Cocoa Observers
Panther introduced Key-Value Observing, a Cocoa implementation of the observer pattern. It’s very useful, but the API kind of sucks.
To get observation notices, you have to override a lengthy selector (observeValueForKeyPath:ofObject:change:context:), provide a static context pointer, and essentially implement a big switch statement on the key path.
That’s unwieldy, but I think it also makes for unmaintainable code: the callbacks end up thrown in the same method, and they’re separated from the observer registration.
KVO+Blocks is an NSObject category I wrote which provides addObserverForKeyPath:task:, where the latter parameter is a block.
So, before KVO+Blocks:
const static NSString *SomeValueObservationContext =
@"org.andymatuschak.SomeValueObservationContext";
- (void)registerObservation
{
[observee addObserver:self
forKeyPath:@"someValue"
options:0
context:SomeValueObservationContext]
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == SomeValueObservationContext &&
[keyPath isEqualToString:@"someValue"])
{
NSLog(@"someValue changed: %@", change);
}
else
{
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
- (void)dealloc
{
[observee removeObserver:self forKeyPath:@"someValue"];
[super dealloc];
}
And after:
- (void)registerObservation
{
[observee addObserverForKeyPath:@"someValue"
task:^(id obj, NSDictionary *change) {
NSLog(@"someValue changed: %@", change);
}];
}
Note that you don’t even need to unregister your observer; it’s handled for you when the observee is released. If you want to unregister early, there’s API for that too.
Blocks make life happy, ladies and gentlemen. More on this later.