KVO实现畅想

前言:已经有一些很成熟的文章介绍kvo的原理iOS底层原理总结,英文好的同学可以直接看这篇。这里只是笔者的一些遐想,如果我设计kvo,我要怎么做。

把大象装进冰箱需要几步:

需要实现的功能:实时获取到属性变更,并通知观察者
第一步:需要知道这个属性变化了
第二步:把属性的变化送到观察者中去

第一步:

方法1 - 手动重写set方法:

属性变化被通知,让我实现,我肯定想的是在set方法中做文章。重写set方法,调用外部的类去传达这个变化。
但是这个需要重写set方法才能支持kvo。这样使用kvo的业务层会很头疼,每个能被观察的属性都需要重写set方法。

方法2 - Method Swizzling:

使用runtime功能,调换set方法,实现自动重写。这个需要在进程加载的时候使用runtime统一去做。
这种弊端太明显。业务层也有可能进行Method Swizzling,如果业务层swizzle后不调用原方法,kvo就失效了。而且一旦发生问题,很难调试。

方法3 - 编译时处理:

像ARC一样,在编译的时候手动给set方法加上需要的处理

这样实现的话,所有实例的属性变化的时候都会往外面调用方法,告诉外面我的值变了,不管该属性有没有被观察。这就需要中间加一层,查找该属性有没有被观察。所以每次类属性赋值,都要查询一遍观察者表。

系统的解决方案:

系统的方案是在运行时解决这件事。在每次属性被观察的时候,生成一个子类模版,子类模版里覆写set方法,并且把被观察的对象isa指针指向自动生成的子类。系统的方案优于方案三的地方是,减少了观察者表的查询。只有属性被观察了,属性的改变才会去查询观察者表。

第二步:

这里没查到相关资料,不过这步也比较简单了。以下是笔者猜想的实现逻辑:

需要一个观察者表,在add observer的时候,把观察者添加到以被观察者地址为key的一个表里。

{
  "被观察者地址 + keyPath": ["观察者地址", "观察者地址"]
}

这样每次被观察者的属性发生变化的时候,就去表里寻找自己相关属性的观察者,然后调用观察者的observeValueForKeyPath方法。

观察者是一个数组,这样可以允许多个观察者同时观察一个对象。这个数组是不去重的,实际使用kvo的时候会发现,如果对同一个属性的add observer两次,observeValueForKeyPath就会执行两次。

"被观察者地址 + keyPath" 是为了区分观察者观察的是哪一个属性,防止没有观察的属性发生变化也调用观察者的observeValueForKeyPath。

你可能感兴趣的:(KVO实现畅想)