KVO原理

作者: HChase | 来源:发表于2019-07-15 19:18 被阅读0次

    KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变;

    KOV的应用

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.person = [[Person alloc] init];
        self.person.age = 10;
        
        [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.person.age = 20;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        
        NSLog(@"%@ ---- %@", keyPath, change);
    }
    
    • 对象添加监听器(addObserver: forKeyPath: options: context),可设置监听旧值(Old)或新值(New);
    • 重写回调方法(observeValueForKeyPath: ofObject: change: context:),就可以监听对象属性的变化;

    原理

    没有设置kvo的对象
    设置kvo的对象
    • 给对象的属性设置kvo,此时runtime将创建一个类名为NSKVONotifying_×××的类(如NSKVONotifying_Person),该类为Person的子类;然后将实例对象的isa指针指向该类;
    • 该类将重写已被设置kvo的setter方法(如setAge:);class方法返回的是父类的类对象(如: [Person class]);

    NSKVONotifying_×××的setter方法

    上图中setAge:方法,内部调用了Foundation的_NSSetIntValueAndNotify方法;伪代码如下:

    - (void)setAge:(int)age
    {
        _NSSetIntValueAndNotify();
    }
    
    // 伪代码
    void _NSSetIntValueAndNotify()
    {
        [self willChangeValueForKey:@"age"];
        [super setAge:age];
        [self didChangeValueForKey:@"age"];
    }
    
    - (void)didChangeValueForKey:(NSString *)key
    {
        // 通知监听器,某某属性值发生了改变
        [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
    }
    

    _NSSetIntValueAndNotify内部实现:

    • 首先调用_NSSetIntValueAndNotify
    • 然后调用父类的setter方法([super setAge:age]);
    • 最后调用didChangeValueForKey,该方法内部回调监听器方法( observeValueForKeyPath:ofObject:change:context:),整个监听过程完成。

    _NSSet***ValueAndNotify

    上述例子是对 int 属性进行监听,所以对应着_NSSetIntValueAndNotify,不同类型的属性对应不同的_NSSet×××ValueAndNotify方法;

    代码验证

    在Person类中,重写willChangeValueForKeydidChangeValueForKey,查看执行状态;

    @implementation Person
    - (void)setAge:(int)age
    {
        _age = age;
        
        NSLog(@"setAge:");
    }
    
    - (void)willChangeValueForKey:(NSString *)key
    {
        [super willChangeValueForKey:key];
        
        NSLog(@"willChangeValueForKey");
    }
    
    - (void)didChangeValueForKey:(NSString *)key
    {
        NSLog(@"didChangeValueForKey - begin");
        
        [super didChangeValueForKey:key];
        
        NSLog(@"didChangeValueForKey - end");
    }
    @end
    
    2019-07-15 19:17:29.235721+0800 KVO[5624:7935847] willChangeValueForKey
    2019-07-15 19:17:29.235880+0800 KVO[5624:7935847] setAge:
    2019-07-15 19:17:29.235975+0800 KVO[5624:7935847] didChangeValueForKey - begin
    2019-07-15 19:17:29.236946+0800 KVO[5624:7935847] age ---- {
        kind = 1;
        new = 20;
        old = 10;
    }
    2019-07-15 19:17:29.237125+0800 KVO[5624:7935847] didChangeValueForKey - end
    

    可以看出监听回调确实是在didChangeValueForKey内部执行的。

    源代码demo

    相关文章

      网友评论

          本文标题:KVO原理

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