KVO 实现原理

addObserver:forKeyPath:options:context:各个参数的作用分别是什么, observer中需要实现哪个方法才能获得KVO回调?

/**
 1. self.person:要监听的对象
 2. 参数说明:
    * @param addObserver  观察者,负责处理监听事件的对象
    * @param forKeyPath 要监听的属性
    * @param  options 观察的选项(观察新、旧值,也可以都观察)
    * @param context 上下文,用于传递数据,可以利用上下文区分不同的监听
 */
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];

/**
 *  当监控的某个属性的值改变了就会调用
 *
 *  @param keyPath 监听的属性名
 *  @param object  属性所属的对象
 *  @param change  属性的修改情况(属性原来的值`oldValue`、属性最新的值`newValue`)
 *  @param context 传递的上下文数据,与监听的时候传递的一致,可以利用上下文区分不同的监听
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"%@对象的%@属性改变了:%@", object, keyPath, change);
}

一、KVO (Key-Value Observing)

KVO 是 Objective-C 对观察者模式(Observer Pattern)的实现。也是 Cocoa Binding 的基础。当被观察对象的某个属性发生更改时,观察者对象会获得通知。

有意思的是,你不需要给被观察的对象添加任何额外代码,就能使用 KVO 。这是怎么做到的?

二、 KVO内部实现原理

  • KVO是基于runtime机制实现的
  • 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
  • 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person
  • 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
  • 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
  • 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类

    KVO内部实现原理.png

三、如何手动触发一个value的KVO

  • 自动触发的场景:在注册KVO之前设置一个初始值,注册之后,设置一个不一样的值,就可以触发了
  • 想知道如何手动触发,必须知道自动触发 KVO 的原理,见上面的描述
  • 手动触发演示
@property (nonatomic, strong) NSDate *now;

- (void)viewDidLoad
{
    [super viewDidLoad];

    // “手动触发self.now的KVO”,必写。
    [self willChangeValueForKey:@"now"];

    // “手动触发self.now的KVO”,必写。
    [self didChangeValueForKey:@"now"];
}

手动触发一个value的KVO.png

四、补充: 如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?(看链接)


五、附注: KVC底层实现原理(如下)

主要分为三大步

  • 第一步:寻找该属性有没有setsetter方法?有,就直接赋值
  • 第二步:寻找有没有该属性的成员属性?有,就直接赋值
  • 第三步:寻找有没有该属性带下划线的成员属性?有,就直接赋值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值