美文网首页
iOS之KVO实现原理探究

iOS之KVO实现原理探究

作者: 内心戏十足的伪胖子 | 来源:发表于2018-09-07 11:03 被阅读53次

    KVO,就是key-value-observing,键值观察者模式。开发中经常会使用到,并且面试大概率问到其底层实现原理。

    用法

    eg:

    #import <Foundation/Foundation.h>
    @interface Programmer : NSObject
    @property (copy, nonatomic) NSString *name;
    @end
    
    #import "Programmer.h"
    @implementation Programmer
    - (void)setName:(NSString *)name {
        _name = name;
    }
    @end
    
    #import "ViewController.h"
    #import "Programmer.h"
    #import <objc/runtime.h>
    
    @interface ViewController ()
    
    @property (strong, nonatomic) Programmer *programmer1;
    @property (strong, nonatomic) Programmer *programmer2;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.programmer1 = [[Programmer alloc] init];
        self.programmer1.name = @"Bob";
        self.programmer2 = [[Programmer alloc] init];
        self.programmer2.name = @"Lili";
        
        NSLog(@"添加监听之前person1的isa指针指向%@",object_getClass(self.programmer1));//在调试模式下可以直接 po self.person1->isa
        NSLog(@"添加监听之前person类对象的isa指针指向%@",object_getClass(object_getClass(self.programmer1)));
        NSLog(@"添加监听之前person1的setName方法的地址%p",[self.programmer1 methodForSelector:@selector(setName:)]);//在调试模式下,通过 p (IMP)地址 打印出这个IMP地址对应的方法名称
        
        [self.programmer1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
        
        NSLog(@"添加监听之后person1的isa指针指向%@",object_getClass(self.programmer1));
        NSLog(@"添加监听之后person类对象的isa指针指向%@",object_getClass(object_getClass(self.programmer1)));
        NSLog(@"添加监听之后person1的setName方法的地址%p",[self.programmer1 methodForSelector:@selector(setName:)]);
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.programmer1.name = @"Mark";
        self.programmer2.name = @"Jeny";
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        NSLog(@"keyPath===%@",keyPath);
        NSLog(@"object===%@",object);
        NSLog(@"change===%@",change);
        NSLog(@"context===%@",context);
    }
    
    1. 定义一个Programmer类,有一个name的property
    2. 然后在控制器中有一个@property (strong, nonatomic) Programmer *programmer1;
    3. 在ViewDidLoad中添加观察,[self.programmer1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];,NSKeyValueObservingOptionNew和NSKeyValueObservingOptionOld分别表示变化之前的值和改变之后的值
    4. 在监听的控制器中实现- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context这个方法,其中change为一个字典,里面包含新值和旧值

    原理

    上面的例子中:

    1. Programmer类的实例programmer1name属性被控制器监听了。这时,OC的runtime机制生成了一个KVONotifying_ Programmer的类
    2. programmer1实例的isa指针从指向Programmer的类对象,变成指向KVONotifying_ Programmer的类对象,而KVONotifying_ Programmer的isa指针指的是KVONotifying_ Programmer的meta-class元类对象,KVONotifying_ Programmer的superclass指针指的是Programmer的类对象
    3. 修改programmer1name属性的时候调用了Foundation框架下的一个_NSSetObjectValueAndNotify方法
    4. KVONotifying_ Programmer类重写了Programmer类属性name的setter方法加入了NSObject的两个方法:willChangeValueForKey:(值改变之前)和didChangevlueForKey:(值改变之后)。在一个被观察属性发生改变之前,willChangeValueForKey:一定会被调用,这就会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而observeValueForKey:ofObject:change:context:也会被调用。

    探究

    待续

    如果有错误,感谢各位大佬指正。

    相关文章

      网友评论

          本文标题:iOS之KVO实现原理探究

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