一个对象的KVO的实现

2016-05-01  本文已影响380人  拾丨玖

Apple 的文档对 KVO 实现的描述:

从Apple 的文档可以看出:Apple 并不希望过多暴露 KVO 的实现细节。不过,要是借助 runtime 提供的方法去深入挖掘,所有被掩盖的细节都会原形毕露:

    - (void)setNow:(NSDate *)aDate
    {
        [self willChangeValueForKey:@"now"]; // 没有必要
        _now = aDate;
        [self didChangeValueForKey:@"now"];// 没有必要
     }

这是完全没有必要的代码,不要这么做,这样的话,KVO代码会被调用两次。KVO在调用存取方法之前总是调用 willChangeValueForKey: ,之后总是调用didChangeValueForkey: 。怎么做到的呢? 答案是通过 isa 混写(isa-swizzling)。第一次对一个对象调用 addObserver:forKeyPath:options:context:时,框架会创建这个类的新的 KVO 子类,并将被观察对象转换为新子类的对象。在这个 KVO 特殊子类中, Cocoa 创建观察属性的 setter ,大致工作原理如下:

   - (void)setNow:(NSDate *)aDate {
        [self willChangeValueForKey:@"now"];
        [super setValue:aDate forKey:@"now"];
        [self didChangeValueForKey:@"now"];
    }

这种继承和方法注入是在运行时而不是编译时实现的。这就是正确命名如此重要的原因。只有在使用KVC命名约定时,KVO才能做到这一点。

KVO 在实现中通过 isa 混写(isa-swizzling) 把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。这在Apple 的文档可以得到印证.

然而 KVO 在实现中使用了 isa 混写( isa-swizzling) ,这个的确不是很容易发现:Apple 还重写、覆盖了 -class 方法并返回原来的类。 企图欺骗我们:这个类没有变,就是原本那个类(这个心机婊)...

但是,假设“被监听的对象”的类对象是 MYClass ,有时候我们能看到对 NSKVONotifying_MYClass 的引用而不是对 MYClass 的引用。借此我们得以知道 Apple 使用了 isa 混写(isa-swizzling)。

上一篇 下一篇

猜你喜欢

热点阅读