KVO 也许是iOS中“最神奇”的部分了,由于你不须要在被观察对象中添加任何代码,就能够实现对被观察对象属性改变的通知。KVO到底是怎么实现的? html
KVO是经过Objective-C的runtime来实现的。当你第一次要对一个对象进行观察时,runtime会为你建立一个被观察对象class的subclass。在这个新建立的subclass中,KVO会复写所要观察属性的setter方法,而后转换被观察对象的isa指针,指向新建立的subclass,因此,你想要观察的对象,变成了KVO在runtime时建立的subclass。由于Apple不想让这种机制暴露,因此还会复写要观察对象的class方法,因此,当你调用class来判断该对象的class时,还会显示原对象的class类型,而不是subclass的类型。 函数
继续探究 工具
// gcc -o kvoexplorer -framework Foundation kvoexplorer.m #import <Foundation/Foundation.h> #import <objc/runtime.h> @interface TestClass : NSObject { int x; int y; int z; } @property int x; @property int y; @property int z; @end @implementation TestClass @synthesize x, y, z; @end static NSArray *ClassMethodNames(Class c) { NSMutableArray *array = [NSMutableArray array]; unsigned int methodCount = 0; Method *methodList = class_copyMethodList(c, &methodCount); unsigned int i; for(i = 0; i < methodCount; i++) [array addObject: NSStringFromSelector(method_getName(methodList[i]))]; free(methodList); return array; } static void PrintDescription(NSString *name, id obj) { NSString *str = [NSString stringWithFormat: @"%@: %@\n\tNSObject class %s\n\tlibobjc class %s\n\timplements methods <%@>", name, obj, class_getName([obj class]), class_getName(obj->isa), [ClassMethodNames(obj->isa) componentsJoinedByString:@", "]]; printf("%s\n", [str UTF8String]); } int main(int argc, char **argv) { [NSAutoreleasePool new]; TestClass *x = [[TestClass alloc] init]; TestClass *y = [[TestClass alloc] init]; TestClass *xy = [[TestClass alloc] init]; TestClass *control = [[TestClass alloc] init]; [x addObserver:x forKeyPath:@"x" options:0 context:NULL]; [xy addObserver:xy forKeyPath:@"x" options:0 context:NULL]; [y addObserver:y forKeyPath:@"y" options:0 context:NULL]; [xy addObserver:xy forKeyPath:@"y" options:0 context:NULL]; PrintDescription(@"control", control); PrintDescription(@"x", x); PrintDescription(@"y", y); PrintDescription(@"xy", xy); printf("Using NSObject methods, normal setX: is %p, overridden setX: is %p\n", [control methodForSelector:@selector(setX:)], [x methodForSelector:@selector(setX:)]); printf("Using libobjc functions, normal setX: is %p, overridden setX: is %p\n", method_getImplementation(class_getInstanceMethod(object_getClass(control), @selector(setX:))), method_getImplementation(class_getInstanceMethod(object_getClass(x), @selector(setX:)))); return 0; }
而后定义一些工具函数。ClassMethodNames 经过Objective-C 的runtime函数,来返回当前class实现的方法名。 spa
代码执行结果 指针
control: <TestClass: 0x104b20> NSObject class TestClass libobjc class TestClass implements methods <setX:, x, setY:, y, setZ:, z>
x: <TestClass: 0x103280> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA> code
y: <TestClass: 0x104b00> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA> component
xy: <TestClass: 0x104b10> NSObject class TestClass libobjc class NSKVONotifying_TestClass implements methods <setY:, setX:, class, dealloc, _isKVOA> orm
Using NSObject methods, normal setX: is 0x195e, overridden setX: is 0x195e server
Using libobjc functions, normal setX: is 0x195e, overridden setX: is 0x96a1a550 htm
能够看出,0)TestClass 在runtime时变成了NSKVONotifying_TestClass
1)虽然x,y只观察了一个属性,可是NSKVONotifying_TestClass却实现了setY, setX方法。也就是说,一个类,KVO只会subclass一个KVO类,也就是NSKVONotifying_TestClass类。
2)NSKVONotifying_TestClass 覆写了class方法,来掩盖subclass的存在,还覆写了dealloc方法。除此以外,还有一个新的方法_isKVOA, 是Apple提供的一个私有方法,用于判断一个object是否生成动态subclass。
原文连接:http://www.mikeash.com/pyblog/friday-qa-2009-01-23.html