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