浅谈iOS KVO

  • Post author:
  • Post category:其他




1、KVO

<Key-Value-Observing>

顾名思义,键值监听,可以用于监听某个对象属性值的变化。

KVO

是一个非正式协议,提供了一个途径,使对象(观察者)能够观察其他对象(被观察者)的属性,当被观察者的属性发生变化时,观察者就会被告知该变化。首先了解一下

KVO

的基本使用,然后在此基础上,我们深入了解一下

KVO

的底层实现原理。

//给一个对象属性添加KVO监听
[self addObserver:(nonnull NSObject *)
       forKeyPath:(nonnull NSString *)
          options:(NSKeyValueObservingOptions)
          context:(nullable void *)]
//当监听对象的属性值发生改变时,就会调用
-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                      context:(void *)context
//需要在不使用的时候,移除监听
- (void)dealloc{
    [self removeObserver:self forKeyPath:@""];
}



2、


KVO的底层实现原理


在了解

KVO

底层之前,我们先要对

isa

一些基本的概念有个了解,

instance对象的isa指向class对象,class对象的isa指向Meta-Class对象,Meta-Class对象的isa指向基类的Meta-Class对象

。如果不了解的可以去看一下我的另一篇文章

《通俗易懂的ios方法调用底层原理》



KVO 是基于Runtime机制实现的,KVO是运用了一个

isa-swizzling

技术,就是类型混合指针机制, 将2个对象的isa指针互相调换, 就是俗称的黑魔法.

  • 当某个类的属性对象

    第一次被观察

    时,系统就会在运行期

    动态

    地创建

    该类的一个派生类

    ,在这个派生类中重写基类中被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的

    通知机制

  • 如果原类为Person,那么生成的派生类名为

    NSKVONotifying_Person

  • 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

  • 调用非监听属性设置方法,会通过

    NSKVONotify_Person



    superclass

    ,找到

    Person

    类对象,再调用其 实例方法

  • 键值观察通知依赖于NSObject 的两个方法:

    willChangeValueForKey:



    didChangevlueForKey:

    在一个被观察属性发生改变之前,

    willChangeValueForKey:

    一定会被调用,这就 会记录旧的值。而当改变发生后,

    didChangeValueForKey:

    会被调用,继而

    observeValueForKey:ofObject:change:context:

    也会被调用。

  • 补充:KV0的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类。如果没有重写

    class

    方法,当该对象调用

    class

    方法时,会在自己的方法缓存列表,方法列表,父类缓存,方法列表一直向上去查找该方法,因为

    class

    方法是

    NSObject

    中的方法,如果不重写最终可能会返回

    NSKVONotifying_Person

    ,就会将该类暴露出来,也给开发者造成困扰,写的是

    Person

    ,添加KVO之后

    class

    方法返回怎么是另一个类。



3、kvo


和 notification(通知)的区别?


  • KVO



    NSNotificationCenter

    都是

    iOS

    中观察者模式的一种实现。区别在于,相对于被观察者和观察者之间的关系,

    KVO

    是一对一的,而不是一对多的。

    KVO

    对被监听对象无侵入性,不需要修改其内部代码即可实现监听。

  • notification 的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,而且可以一对多。



4、


测试代码

NSKeyValueObservingOptions option = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
Person *person1 = [Person new];

NSLog(@"person1添加KVO监听对象之前-类对象 -%@", object_getClass(person1));
NSLog(@"person1添加KVO监听之前-方法实现 -%p", [person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person1添加KVO监听之前-元类对象 -%@", object_getClass(object_getClass(person1)));

[person1 addObserver:self forKeyPath:@"age" options:option context:@"age chage"];

NSLog(@"person1添加KVO监听对象之后-类对象 -%@", object_getClass(person1));
NSLog(@"person1添加KVO监听之后-方法实现 -%p", [person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person1添加KVO监听之后-元类对象 -%@", object_getClass(object_getClass(person1)));

//打印结果
KVO-test[1214:513029] person1添加KVO监听对象之前-类对象 -Person
KVO-test[1214:513029] person1添加KVO监听之前-方法实现 -0x100411470
KVO-test[1214:513029] person1添加KVO监听之前-元类对象 -Person

KVO-test[1214:513029] person1添加KVO监听对象之后-类对象 -NSKVONotifying_Person
KVO-test[1214:513029] person1添加KVO监听之后-方法实现 -0x10076c844
KVO-test[1214:513029] person1添加KVO监听之后-元类对象 -NSKVONotifying_Person



版权声明:本文为shuzhi57原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。