您的位置:首页 > 其它

kvo底层的一些实现

2016-06-09 00:00 288 查看
一直痴痴地使用kvo,一直想了解一下其实现机制。

1.kvo的基本思想

kvo的来源于设计模式其中的观察者模式,观察者模式将观察者和被观察者之间的耦合度降低。我自己理解为观察者模式是kvo的大纲,也解释了为什么我们要手动单独实现回调方法。它本身的一个思路为被观察对象在自身状态改变的时候,通知观察者。通知是调用观察者对象类中的接口方法实现。

想想我刚开始接触kvo是,傻傻的分不清楚,跟着网上指导,先注册,后回调方法,再移除。写完后懵逼依旧,第二次想用,打开火狐浏览器,输入 kvo的使用,点击,然后再傻不愣登的学半天,吃个饭回来又忘了当下菜用了。

其实按照设计模式的思路理解能更好的理解,为什么要这么一步步的干。

2.kvo实现的要素

通过以下两个方法可以实现观察时的注册和移除,但要实现kvo是有条件的。

[code=language-objectivec]- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

曾经有人给我告白,kvo的实现需要有setter方法。那时候,听到以后自己是懵逼的,在看了许多资料后才有一点点思路。

将观察者和被观察者注册好之后,被观察着满足以下两个条件任何一种,观察着才会得到通知。

a、遵循使用属性的setter方法,也就是说,此属性需要有setter方法。当使用@property时,系统自动为我们生成了sette方法,此时我们可以直接使用。但当使用实例变量的方式的时候,需要我们自己重写setter方法。

b、当没有setter方法的时候我们需要通过key-path这个方法来设置。如下:

[code=language-objectivec]
[class setValue:30 forKey:@"age"];

3.kvo的实现原理

从我认识kvo那天起,就一直很奇怪一个问题。明明我自己在写类中的属性的时候并没任何关于观察的代码,实现观察的东西是怎么添加上去的。在学习到runtime的时候,才懂了,kvo的实现离不开强劲的runtime。下面我们一步步来:

a、kvo会生效其内部的方法和逻辑,其原因为下面两个方法:

[code=language-objectivec]- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;

两个方法用于通知系统观察的属性即将要变和已经变完,这两个方法被写入属性的setter方法中。

[code=plain]- (void) setAge:change
{
[self willChangeValueForKey:@"age"];
age = change;
[self didChangeValueForKey:@"age"];
}

b、kvo的添加

当我们使用kvo进行观察的时候,系统已经重写了对应属性的setter方法。系统做了两件事情:

第一件为当我们对相关属性进行观察的时候,利用runtimer帮我们生成一个派生类,这个类是对被观察者对象中isa指针指向类的一个扩充(如果不清楚isa指针、实例对象、类、元类、根元类可以暂且理解为对系统利用runtime帮我们生成了一个比被观察者对象类更强大的类)。扩充的内容就是重写相关属性(注册观察的属性)的setter方法,就类似于a中对setter方法的处理一样。

第二件为系统接近于“卑鄙下流(开个玩笑)”的连哄带骗的使用它自己生成的派生类代替我们原先自己的类。其连哄带骗的方式为利用runtime动态的修改了isa指针指向的类。从而导致于我们通过setter方法改变属性值的时候,其调用的方法为系统帮我们重写setter方法即为派生类中的setter方法。此方法中有关于观察通知的相关方法,从而触发一些列事件。

注:如果对替换过程有兴趣的,可以了解runtime动态创建对象和如何替换对象。

至此就是kvo的添加过程,如果有出错的地方,十分欢迎指出,经常被打脸,再被打中成长。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kvo 底层 添加