iOS KVO實現方式

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;
    }



首先,定義一個TestClass,有3個屬性。

而後定義一些工具函數。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

相關文章
相關標籤/搜索