KVO原理探索

作者: Breezes | 来源:发表于2020-08-05 09:57 被阅读0次

    当一个类对某个对象的某个属性进行KVO监听时,系统为自动为该对象生成一个新的类,并把该对象的isa指针指向该类,以下面的Person类为例,通过代码看一下:

    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    @end
    

    使用object_getClass方法分别获取person对象在KVO监听前后所属的类

    Person *person = [Person new];
    NSLog(@"observing before = %@",object_getClass(person));
    [person addObserver:self forKeyPath:@"name" options:  
    NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    NSLog(@"observing after = %@",object_getClass(person));
    

    打印结果⬇️

    2020-08-01 15:47:51.387800+0800 KVO原理探索[2812:151048] observing before = Person
    2020-08-01 15:47:51.388244+0800 KVO原理探索[2812:151048] observing after = NSKVONotifying_Person
    

    但是通过打印person对象会发现:


    image.png

    person还是属于Person类,这是因为派生类NSKVONotifying_Person重写了Class方法,以欺骗调用者。

    那么接下来如何确定person的isa指针被重新指向了NSKVONotifying_Person?

    交换指针需要使用object_setClass方法,通过条件断点检测设置KVO后是否调用object_setClass

    image.png
    详细文章请参考:https://juejin.im/post/6855129007928082446

    生成派生类后是如何发送通知给监听者?

    通过断点看一下派生类的setName方法:


    image.png

    通过上面的输出,我们了解到NSKVONotifying_Person类的setter方法转换为Foundation框架的_NSSetObjectValueAndNotify函数。
    继续看堆栈信息,


    image.png
    通过堆栈信息,我们看出KVO的通知调用顺序:
    1. -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] 
    2. -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:] 
    3.  -[Person setName:]
    4.  NSKeyValueDidChange ()
    5.  NSKeyValueNotifyObserver ()
    6.  -[ViewController observeValueForKeyPath:ofObject:change:context:] 
    
    

    总结一下:

    使用KVO监听的person对象的isa指向了NSKVONotifying_Person类对象,那么他的set方法就在此类对象中,而且NSKVONotifying_Person类是Person的子类,当调用NSKVONotifying_Person的setName方法时,就会调用Foundation框架中的_NSSetObjectValueAndNotify方法。
    _NSSetObjectValueAndNotify方法内部实现是:
    首先调用willChangeValueForKey:
    调用父类的setName:方法
    调用didChangeValueForKey:当调用这个方法时内部就会触发监听器Oberser的监听方法observeValueForKeyPath:ofObject:change:context:这时候就能知道监听对象的属性值的改变了。

    相关文章

      网友评论

        本文标题:KVO原理探索

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