Mike Ash posted a great article about problems with Cocoa’s Key Value Observing and provided a better set of classes to work with KVO: MAKVONotificationCenter. I’ve been using Mike’s code in various projects over the last few months and am a big fan.
When I started development on MOO I knew I wanted the project to use Mike’s code but I also wanted to take advantage of Cocoa’s Garbage Collection and Snow Leopard’s blocks. I originally tried to retrofit MAKVONotificationCenter to work with GC and with blocks but it rapidly become a very difficult task. So I wrote my own KVO Notification class that was fully GC compatible and worked purely with blocks (no delegate methods at all).
Jumping as quickly into the code as I can, here is an example showing a KVO block that modifies a CoreAnimation layer’s position in responses to changes in the application’s model (example code adapted from MOO’s CGameMapView.m):
void *theBlock = ^(NSString *keyPath, CALayer *self, NSDictionary *change, id identifier)
{
self.position = CGPointMake(self.viewPoint.scale * self.modelObject.position.x, self.viewPoint.scale * self.modelObject.position.y);
};
CALayer *theLayer = ...;
[theLayer addKVOBlock:theBlock forKeyPath:@"modelObject.position" options:0 identifier:@"KVO_IDENTIFIER_1"];
[theLayer addKVOBlock:theBlock forKeyPath:@"viewPoint.scale" options:0 identifier:@"KVO_IDENTIFIER_2"];
I find this, blocks based, style of KVO handling much cleaner and easier to both code and maintain. No worrying about selectors and targets and passing whatever data you need into the delegate method. You just declare your block and register it with the KVO Notification Center.
Clean-up (unregistration) is also straight-forward, although with Garbage Collection enabled applications this isn’t usually even necessary.
If you’re sharp-eyed you might notice that I’ve called one of the block parameters “self”. By providing my own ’self’ parameter I am obscuring and prevent access to the parent method’s ’self’ parameter. The reason I am doing this is because I’ve found it is very common to register for notifications for “self” and unless you’re very careful you can end up with a horrible retain cycle caused memory leak. By providing my own self parameter I obscure the parent method’s self and make sure these kind of retains cycles don’t happen.
The code is released under the BSD License on bitbucket (mercurial): http://bitbucket.org/schwa/kvoblocknotificationcenter/. You’ll obviously need use this on Snow Leopard or possibly with Landon Fuller’s plblocks toolchain (although I haven’t tried that myself).
(Update: I’ve posted a movie on flickr of KVO Block Observing in action) (update 2: Joachim Bengtsson has posted a great HOWTO guide for blocks. It also goes into some detail about the “self” problem I mentioned and a better solution (__block variables)