Key-Value Observing on classes No Comments
In the course of experimentation, I just discovered a neat trick. You can use key-value observing on a class object! Consider this simple example:
#import <Cocoa/Cocoa.h> static int Test_x = 0; @interface Test : NSObject { } + (int)x; + (void)setX:(int)x_; @end @implementation Test + (int)x { return Test_x; } + (void)setX:(int)x_ { [self willChangeValueForKey:@"x"]; Test_x = x_; [self didChangeValueForKey:@"x"]; } @end @interface Observer : NSObject { } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; @end @implementation Observer - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; { printf("observed for %s, value = %d\n", [keyPath UTF8String], [Test x]); } @end int main(int argc, char **argv) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; Observer *o = [[[Observer alloc] init] autorelease]; [[Test class] addObserver:o forKeyPath:@"x" options:NSKeyValueObservingOptionInitial context:NULL]; [Test setX:99]; [[Test class] removeObserver:o forKeyPath:@"x"]; [pool drain]; return 0; }
And it works!
$ gcc kvotest.m -o ./kvotest $ ./kvotest observed for x, value = 0 observed for x, value = 99 $
It has some caveats, of course:
- Automatic notification doesn’t work, probably for some reason having to do with the way the runtime implements class objects and the method swizzling KVO does to make automatic notifications work. Your setters have to explicitly call
-willChangeValueForKey:and-didChangeValueForKey: - This is a hack. It’s not documented to work at all and thus Apple is under no obligation to see that it keeps working.
- You’re not supposed to do things like this with classes. You’re supposed to define a singleton object, e.g.
[NSNotificationCenter defaultCenter]and use that. - I’ve only tried this on Snow Leopard (though it was compiled against the Leopard SDK). I don’t have anything else handy to test with.
- I haven’t tried this with any of the more advanced KVO features (to-many relationships, dependent keys, NSKeyValueObservingOptionPrior).
- Obviously, as shown this isn’t thread-safe, though that’s more for the use of the
staticvariable to hold the value.
The only use for it I can think of offhand is when you have a class which keeps a list of its objects and provides a method for accessing that list as an array, and you want some other object to be notified when that list changes (for example, one could in theory want to observe changes in NSValueTransformer’s +valueTransformerNames). The documentation explicitly states, “Instead of observing an array, observe the ordered to-many relationship for which the array is the collection of related objects.”,
so to see changes in the list by KVO, one has to observe the property of the class itself. Classes can’t have properites (or static instance variables, come on Apple), so something like this is one way of doing the trick.
Use at your own risk!
March 12, 2010 at 5:24 pm