美文网首页
KVO实现原理

KVO实现原理

作者: 帽子和五朵玫瑰 | 来源:发表于2018-08-08 09:39 被阅读0次

    KVO实现原理

    一,概述

    KVO,Key-value Observing,它提供一种机制,当制定的对象的属性被修改后,其观察者就会接收到通知,简单的说就是每次指定的被观察的对象的属性被修改后,KCO就会自动通知相应的观察者了。

    KVO其实也是观察者模式的一种应用,这种模式有利于两个类之间的解耦合。

    二/KVO的具体实现

    @interface ViewController ()
    
    @property (strong, nonatomic) Person *p1;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad { 
        [super viewDidLoad]; 
        // 1.什么是通知 
        // 3个对象 
        self.p1 = [[Person alloc] init];
        self.p1.name = @"p1"; 
        //打印监听前类信息 
        [p1 printInfo]; 
        // KVO是监听对象的属性值的改变的 
        [self.p1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; 
        self.p1.name = @"123"; 
        //打印监听后类信息 
        [p1 printInfo]; 
        [p1 removeObserver:self forKeyPath:@"name"]; 
        //打印移除监听后类信息 [p1 printInfo]; } 
        // 这个方法时属于 NSObject 类的,任何对象都可以作为观察者 
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { NSLog(@"监听到了%@的%@属性发生了改变", object, keyPath); NSLog(@"%@", change); }
    
    @end
    
     person类方法: 
     -(void)printInfo { 
        NSLog(@"isa:%@, supper class:%@", NSStringFromClass(object_getClass(self)), class_getSuperclass(object_getClass(self))); 
        NSLog(@"self:%@, [self superclass]:%@", self, [self superclass]); 
        NSLog(@"age setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setAge:))); 
        NSLog(@"name setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setName:))); 
        NSLog(@"printInfo function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(printInfo))); }
    
    

    三、KVO的实现原理

    KVO 是基于运行时实现的 isa Class NSKVONotifying_Person

    基本的原理:当观察某对象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,从而激活键值通知机制。

    ②子类setter方法剖析:KVO的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用2个方法:被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;之后observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter 方法这种继承方式的注入是在运行时而不是编译时实现的。

    上述例子中,当 p1.name 的值改变时,p1对象的 isa 指针会指向 NSKVONotifying_Person,意味着,在程序运行时,会动态生成一个 NSKVONotifying_Person 类,该类继承于 Person,而且该类中也有个 -setName: 方法,方法中在设置 name 的同时实现了:

    四、特点

    观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行KVO的回调方法,例如是否执行了setter方法、或者是否使用了KVC赋值。

    如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制,更加不会调用回调方法的。

    所以使用KVO机制的前提是遵循 KVO 的属性设置方式来变更属性值。

    拓展

    1.KVC与KVO的不同?

    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实现原理

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