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

相关文章
相关标签/搜索