KVO的本质是什么?

KVO

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

基本应用

在ViewController中监听Person对象的age变化,点击控制器屏幕就改变age值,可以看到收到变化通知.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.p1 = [[Person alloc] init];
    self.p1.age = 10;
    
    [self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"age change"];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.p1.age = 12;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}

点击屏幕可以看到打印

2019-05-07 21:49:11.190686+0800 KVO[38048:2769151] {
    kind = 1;
    new = 12;
    old = 10;
}

KVO探究

添加KVO之前的Person

在添加监听前断点查看Person对象


添加KVO之前

添加KVO之后查看Person对象

添加KVO之后

比对添加KVO添加前后发现,Person对象的类信息发生了改变.p1由Person对象变成了NSKVONotifying_Person对象.

打印下KVO前后方法列表变化

- (NSString *)getMethodList:(Class )cls{
    unsigned int count;
    Method *methods = class_copyMethodList(cls, &count);
    
    NSString *names = @"";
    for (int i = 0 ; i < count; i ++) {
        Method method = methods[i];
        NSString *name = NSStringFromSelector(method_getName(method));
        names = [NSString stringWithFormat:@"%@_%@",names,name];
    }
    return names;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.p1 = [[Person alloc] init];
    self.p1.age = 10;
   
    NSLog(@"KVO之前-->%@",[self getMethodList:object_getClass(self.p1)]);
    
    
    [self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"age change"];
    
    NSLog(@"KVO之后-->%@",[self getMethodList:object_getClass(self.p1)]);
}

打印结果:

2019-05-07 22:23:09.775530+0800 KVO[39211:2801239] KVO之前-->_setAge:_age
2019-05-07 22:23:09.775838+0800 KVO[39211:2801239] KVO之后-->_setAge:_class_dealloc__isKVOA

多了三个方法:
1.class:这个也间接证明为啥用实例对象获取class会总是Person
2.dealloc
3.isKVOA

内部实现类似这样:

 (void)setAge:(int)age
{
    _NSSetIntValueAndNotify();
}

// 屏幕内部实现,隐藏了NSKVONotifying_Person类的存在
- (Class)class
{
    return [Person class];
}

- (void)dealloc
{
    // 收尾工作
}

- (BOOL)_isKVOA
{
    return YES;
}

所以对比两个类的结构:


两个类结构对比.png

总结

  • 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
  • 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
    1.willChangeValueForKey:
    2.父类原来的setter
    3.didChangeValueForKey:
    内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,799评论 1 32
  • 1.设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类...
    司马DE晴空阅读 5,118评论 0 7
  • 问题 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 如何手动触发KVO ? 首先需要了解KVO...
    hjltony阅读 3,624评论 0 2
  • 对小码哥底层班视频学习的总结与记录。面试题部分,通过对面试题的分析探索问题的本质内容。 问题iOS用什么方式实现对...
    xx_cc阅读 13,676评论 26 65
  • 第54天参加宇彤老师第13期语言美学60天打卡营的练习训练,今天的练习是:绕口令,哥挎瓜筐,练了好多遍,有2个字普...
    沛珝阅读 2,999评论 0 0