美文网首页
KVO的底层实现原理

KVO的底层实现原理

作者: 6ffd6634d577 | 来源:发表于2017-02-14 11:57 被阅读79次
    什么是KVO ?

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

    KVO的使用比较简单,基本上都是三步:

    1. 注册观察者
      addObserver:forKeyPath:options:context:
    1. 观察者中实现
      observeValueForKeyPath:ofObject:change:context:
    2. 移除观察者
      removeObserver:forKeyPath:

    接下来我们通过一个小Demo来探索KVO的底层实现:

    1.新建工程后创建一个Person类
    
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    
    @property(nonatomic,copy)NSString *name;
    
    @end
    -----------------------------------------
    #import "Person.h"
    
    @implementation Person
    
    @end
    

    控制器里的代码如下:

    #import "ViewController.h"
    #import "Person.h"
    
    @interface ViewController ()
    
    @property(nonatomic,strong)Person *p;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        Person * p =[[Person alloc] init];
        self.p = p;
        p.name = @"张三";
        p.height = 170;
        
        //添加KVO 监控 p 当中 属性height 的改变
        [p addObserver:self forKeyPath:@"height" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
        
        p.height = 180;
    }
    
    //监控属性变化触发的方法..
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"height"]) {
            
            NSLog(@"%@",change);
            
        }
    }
    
    
    -(void)dealloc
    {
        [self.p removeObserver:self forKeyPath:@"height"];
    }
    

    第一次在添加监听者的时候打断点p对象的isa指针如下:

    4580E4B2-32C8-40C5-9B9F-CFAE21F9173F.png

    再往下走一步的时候p的isa指针如下:

    A30651A7-4950-4BE8-B0C3-A8D04FB82E75.png

    再往下走的时候就会出发KVO的监听方法

    B1F8ECED-78D1-491C-B3A4-D1F69740EFC8.png

    接下来我们用setValue:forKey(KVC)方法给p的属性赋值:

    //    p.height = 180;
        [p setValue:@(180) forKey:@"height"];
    

    控制台打印结果如下:

    40F329D8-BA1B-4116-9AC6-0700E4F655E6.png

    接下来我们测试一下直接给p的属性赋值,不通过set方法:

    在Person类中添加一个方法:
    -(void)changeHeight
    {
        _height = 200;
    }
    
    在控制器类中:
    //    p.height = 180;
    //    [p setValue:@(180) forKey:@"height"];
        [p changeHeight];
    

    控制台没有打印任何信息,说明没有触发KVO的监听方法:

    C60729A0-0111-46CA-BA73-E00C1A140751.png
    总结:

    可见,在addObserver:forKeyPath:options:context:之后,对象p的isa变为了NSKVONotifying_Person。
    所以,根据上面的isa介绍,响应setAge的方法,实际上是对象p的isa即NSKVONotifying_Person类的setAge方法,并不是原Person类的setAge方法。
    可以大胆想象,NSKVONotifying_Person类是Person类的子类,在NSKVONotifying_Person类内部,重写了setAge方法,并且在setAge方法里让监听器调用了observeValueForKeyPath:ofObject:change:context:方法。
    那么,NSKVONotifying_Person是怎么产生的呢?简单的说,是由runtime产生的。

    相关文章

      网友评论

          本文标题:KVO的底层实现原理

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