oc-KVO的原理理解

最近在看kvo的原理,网上有很多前篇一律的文章,当然也有几篇写的非常好的。
比如https://www.jianshu.com/p/cf079e5433e4和https://www.jianshu.com/p/badf5cac0130

以下是自己的一些理解:

什么是KVO?

KVO是苹果的观察者模式的一个实现。一个对象可以添加监听者来监听自己的一些状态变化。
比如:

[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

person对象添加了监听者self。当person对象的属性发生变化的时候,'self'就可以拿到这个变化。

怎么实现的?

在了解kvo的原理之前,我们先思考一下,如果是我们来做这件事,应该怎么做?
我觉得应该有以下几步:

  • person对象内部维护一个数组或者表,用来存放监听者和监听属性
  • 重写person对象的setter方法,当setter方法传过来的参数和之前的信息不一样的时候,我们就想办法通知到监听者。

我觉得上面的思路应该很好实现,因为难点不多。
但是对比系统的做法,感觉代码上不够简洁和高大上。

系统的做法是:

  • 当给一个对象添加监听者的时候,会动态生成一个新的类,类名是 NSKVONotifying_+当前类名

  • 将当前对象的isa指针指向这个新生成的子类
    为什么要改isa指针?因为我们用对象调用方法的时候,是通过对象的isa指针找到对象所属的类,然后在类的方法列表里找方法。所以如果要调用新生成的子类的setter方法,就需要更改这个。

  • 重写当前对象的class方法重写,使其看起来仍然是之前的类

  • 当对象的属性发生变化的时候。新生成的子类setter方法开始调用

    • 修改之前调动willChangeValueForKey:
    • 调用父类的setter方法
    • 修改之后调用didChangeValueForKey:
  • didChangeValueForKey:调用后,就会发送通知。监听者的observeValueForKeyPath:ofObject:change:context:方法将会调用

看到这里,不知道大家会不会和我一样有一个疑惑。未实现就会crash。但是为什么不加方法实现的判断?如果加了不就可以预防crash?
后来仔细想了想,因为observeValueForKeyPath:ofObject:change:context:是非正式协议。基本上可以理解为是NSObject系所固有的方法,任何该系列的类都有这个方法。本身系统对方法实现与否都不关心,如果真的要对这个方法加预防处理,那么其实所有的方法都应该加预防。这样就不会有后面的消息转发机制了。
所以这样一想,也就能理解为什么系统不加预防判断了。 这些东西其实就是开发者自己应该关注的问题,而不是系统。

还有一点想要分享

如果自己想手动实现kvo,不光光是要重写setter方法,加上willChangeValueForKey:didChangeValueForKey:
如果只是这样写了,会发现监听的方法将被调用两次。
调用两次的原因是,系统本身会触发kvo一次,然后我们手动也触发了一次。
所以应该实现+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;方法,对于我们要手动触发的kvo,返回NO

你可能感兴趣的:(oc-KVO的原理理解)