Objective-C 学习笔记 | KVO(key-value obsereving)

Objective-C 学习笔记 | KVO(key-value obsereving)

Objective-C 学习笔记 | KVO(key-value obsereving)

KVO 是指当指定的对象的属性被修改时,允许对象接收通知的机制。

使用 KVO

创建一个 BNRObserver,实现 lastTime 发生变化时的回调方法:

#import "BNRObserver.h"

@implementation BNRObserver

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSString *oldValue = [change objectForKey:NSKeyValueChangeOldKey];
    NSString *newValue = [change objectForKey:NSKeyValueChangeNewKey];
    NSLog(@"Observed: %@ of %@ was changed from %@ to %@", keyPath, object, oldValue, newValue);
}

@end

在 main.m 中创建一个 BNRObserver 实例,让它观察 logger 的 lastTime 属性:

__unused BNRObserver *observer = [[BNRObserver alloc] init];
// 一旦 logger 的 lastTime 发生变化,通知 observer 改变的新值和之前的旧值
[logger addObserver:observer forKeyPath:@"lastTime"
    options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

如果使用存取方法来设置属性,那么系统会自动通知观察者,上述方法有效。

如果直接给实例变量赋值,可以通过 willChangeValueForKey: 和 didChangeValueForKey: 方法显式通知系统某个属性的值即将/已经改变。

// 动作方法总有一个实参,它是传入发送动作消息的对象
- (void)updateLastTime:(NSTimer *)timer
{
    NSDate *now = [NSDate date];
    // [self setLastTime:now];
    [self willChangeValueForKey:@"lastTime"];
    _lastTime = now;
    [self didChangeValueForKey:@"lastTime"];
    NSLog(@"Just set time to %@", self.lastTimeString);
}

如果不观察 _lastTime,而是观察 _lastTimeString,我们可以实现一个类方法,告诉系统 _lastTime 会影响 _lastTimeString:

// 类方法的名称 = keyPathsForValuesAffecting + 首字母大写的键的名称(LastTimeString)
+ (NSSet *)keyPathsForValuesAffectingLastTimeString
{
    return [NSSet setWithObject:@"lastTime"];
}

KVO 的工作原理

运行时,如果向某个对象发送 addObserver:forKeyPath:options:context: 方法,它会完成下面三件事:

  1. 决定被观察对象的类,并使用 objc_allocateClassPair() 函数给这个类定义一个新的子类。
  2. 改变对象的 isa 指针,让它指向新的子类(高效改变对象的类型)。
  3. 覆盖被观察对象的存取器,发送 KVO 消息。

请添加图片描述

例如,一个类的 location 属性的存方法代码如下:

// BNRTowel
- (void)setLocation:(NSPoint)location
{
    _location = location;
}

在新的子类中,该方法会被覆盖为:

// KVONotifying_BNRTowel
- (void)setLocation:(NSPoint)location
{
    [self willChangeValueForKey:@"location"];
    [super setLocation:location];
    [self didChangeValueForKey:@"location"];
}

子类的存取器实现会调用原始类的实现,然后将它们用简明的 KVO 通知消息封装起来。这些新的类以及方法都会在运行时使用 Objective-C 运行时函数定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值