美文网首页iOS底层机制技术重塑iOS-Runtime机制
iOS--KVO的实现原理与具体应用(转自看)

iOS--KVO的实现原理与具体应用(转自看)

作者: 超_iOS | 来源:发表于2017-07-17 11:34 被阅读289次

    iOS--KVO的实现原理与具体应用

    长时间不用容易忘,这篇文章挺好的.转载自看
    本文分为2个部分:概念与应用。
    概念部分旨在剖析KVO这一设计模式的实现原理,应用部分通过创建的项目,以说明KVO技术在iOS开发中所带来的作用;
    如果是作为是刚接触KVO的初学者,可以在了解基本原理后粗略看几遍底层实现原理,再认真阅读第二部分的应用内容“学会”怎么去使用KVO,往后再慢慢深入了解KVO这一“黑魔法”技术的实现原理。
    一、KVO是什么?
    KVO 是 Objective-C 对观察者设计模式的一种实现。
    KVO提供一种机制,指定一个被观察对象(例如A类),当对象某个属性(例如A中的字符串name)发生更改时,对象会获得通知,并作出相应处理;【且不需要给被观察的对象添加任何额外代码,就能使用KVO机制】
    在MVC设计架构下的项目,KVO机制很适合实现mode模型和view视图之间的通讯。
    例如:代码中,在模型类A创建属性数据,在控制器中创建观察者,一旦属性数据发生改变就收到观察者收到通知,通过KVO再在控制器使用回调方法处理实现视图B的更新;(本文中的应用就是这样的例子.)
    二、实现原理?
    KVO在Apple中的API文档如下:

    image.png

    KVO 的实现依赖于 Objective-C 强大的 Runtime,从以上Apple 的文档可以看出苹果对于KVO机制的实现是一笔带过,而具体的细节没有过多的描述,但是我们可以通过Runtime的所提供的方法去探索,关于KVO机制的底层实现原理。
    基本的原理:
    当观察某对象A时,KVO机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性keyPath的setter 方法。setter 方法随后负责通知观察对象属性的改变状况。
    深入剖析:
    Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为: NSKVONotifying_A的新类,该类继承自对象A的本类,且KVO为NSKVONotifying_A重写观察属性的setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。
    (备注: isa 混写(isa-swizzling)isa:is a kind of ; swizzling:混合,搅合;)
    ①NSKVONotifying_A类剖析:在这个过程,被观察对象的 isa 指针从指向原来的A类,被KVO机制修改为指向系统新创建的子类 NSKVONotifying_A类,来实现当前类属性值改变的监听;
    所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对KVO的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为“NSKVONotifying_A”的类(),就会发现系统运行到注册KVO的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为NSKVONotifying_A的中间类,并指向这个中间类了。
    (isa 指针的作用:每个对象都有isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为观察者时,isa指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象(或实例)了。) 因而在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。
    —>我猜,这也是KVO回调机制,为什么都俗称KVO技术为黑魔法的原因之一吧:内部神秘、外观简洁。
    ②子类setter方法剖析:KVO的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和didChangevlueForKey:,在存取数值的前后分别调用2个方法:
    被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;之后, observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter 方法这种继承方式的注入是在运行时而不是编译时实现的。
    KVO为子类的观察者属性重写调用存取方法的工作原理在代码中相当于:

    image.png

    三、特点:
    观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行KVO的回调方法,例如是否执行了setter方法、或者是否使用了KVC赋值。如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制,更加不会调用回调方法的。
    所以使用KVO机制的前提是遵循 KVO 的属性设置方式来变更属性值。
    四、步骤
    注册观察者,实施监听;
    在回调方法中处理属性发生的变化;
    移除观察者
    ---------------------------------------------------------应用---------------------------------------------------------
    五.实现方法(苹果API文档中的方法):
    A.注册观察者:

    image.png

    [图片上传中。。。(3)]

    B. 属性(keyPath)的值发送变化时,收到通知,调用以下方法:

    image.png

    [图片上传中。。。(4)]

    六、上代码~:
    1.新建项目,UI界面设计如下:第一个是便签,用于显示num数值,关联ViewController并命名为:label;
    第二个是按钮,用于改变num的数值,关联ViewController并命名为:changeNum。

    image.png

    [图片上传中。。。(5)]

    2.模型创建【新建一个File,选择Cocoa Touch Class,命名为“myKVO”,记得选择Subclass of “NSObject”.】代码如下:
    (myKVO.h):

    image.png

    [图片上传中。。。(6)]

    (myKVO.m):

    image.png

    [图片上传中。。。(7)]

    3.在ViewController中监听并响应属性改变。
    (ViewController.h):

    image.png

    [图片上传中。。。(8)]

    (ViewController.m):

    image.png

    [图片上传中。。。(9)]

    调试:便签label初始化没有数值,当每次点击按钮后,label记录的num随之增加,表明按钮使属性num增加的同时,KVO机制发送通知,并调用observeValueForKeyPath:方法使UI更新。
    七、拓展-->
    1.与KVC的不同?
    KVC(键值编码),即Key-Value Coding,一个非正式的Protocol,使用字符串(键)访问一个对象实例变量的机制。而不是通过调用Setter、Getter方法等 显式的存取方式去访问。
    KVO(键值监听),即Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,对象就会接受到通知,前提是执行了setter方法、或者使用了 KVC赋值。
    2.和notification(通知)的区别?
    notification比KVO多了发送通知的一步。
    两者都是一对多,但是对象之间直接的交互,notification明显得多,需要notificationCenter来做为中间交互。而KVO如我们介绍的,设置观察者->处理属性变化,至于中间通知这一环,则隐秘多了,只留一句“交由系统通知”,具体的可参照以上实现过程的剖析。
    notification的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,例如键盘、前后台等系统通知的使用也更显灵活方便。
    3.与delegate的不同?
    和delegate一样,KVO和NSNotification的作用都是类与类之间的通信。但是与delegate不同的是:
    这两个都是负责发送接收通知,剩下的事情由系统处理,所以不用返回值;而delegate 则需要通信的对象通过变量(代理)联系;
    delegate只是一对一,而这两个可以一对多。
    4.涉及技术:
    KVC/KVO实现的根本是Objective-C的动态性和runtime,以及访问器方法的实现;
    总结:
    对比其他的回调方式,KVO机制的运用的实现,更多的由系统支持,相比notification、delegate等更简洁些,并且能够提供观察属性的最新值以及原始值;但是相应的在创建子类、重写方法等等方面的内存消耗是很巨大的。所以对于两个类之间的通信,我们可以根据实际开发的环境采用不同的方法,使得开发的项目更加简洁实用。
    另外需要注意的是,由于这种继承方式的注入是在运行时而不是编译时实现的,如果给定的实例没有观察者,那么KVO不会有任何开销,因为此时根本就没有KVO代码存在。但是即使没有观察者,委托和NSNotification还是得工作,这也是KVO此处零开销观察的优势。

    作者:iOS开发攻城狮链接:http://www.jianshu.com/p/78c9b8a3c303來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

      • 啊左:刚好看完你的文章,文中那篇也是转载我的:https://www.jianshu.com/p/e59bb8f59302
        写了挺久的了,文章一直有修改,你可以看一下,有些内容可能会出错,哈。欢迎交流。
        超_iOS:@啊左 :smile:
        啊左:@_超 客气啦,我不是什么大神,欢迎交流
        超_iOS:多谢大神指导

      本文标题:iOS--KVO的实现原理与具体应用(转自看)

      本文链接:https://www.haomeiwen.com/subject/biyfkxtx.html