KVO原理

作者: HChase | 来源:发表于2019-07-15 19:18 被阅读0次

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

KOV的应用

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person = [[Person alloc] init];
    self.person.age = 10;
    
    [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
    
}

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

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
    NSLog(@"%@ ---- %@", keyPath, change);
}
  • 对象添加监听器(addObserver: forKeyPath: options: context),可设置监听旧值(Old)或新值(New);
  • 重写回调方法(observeValueForKeyPath: ofObject: change: context:),就可以监听对象属性的变化;

原理

没有设置kvo的对象
设置kvo的对象
  • 给对象的属性设置kvo,此时runtime将创建一个类名为NSKVONotifying_×××的类(如NSKVONotifying_Person),该类为Person的子类;然后将实例对象的isa指针指向该类;
  • 该类将重写已被设置kvo的setter方法(如setAge:);class方法返回的是父类的类对象(如: [Person class]);

NSKVONotifying_×××的setter方法

上图中setAge:方法,内部调用了Foundation的_NSSetIntValueAndNotify方法;伪代码如下:

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

// 伪代码
void _NSSetIntValueAndNotify()
{
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key
{
    // 通知监听器,某某属性值发生了改变
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

_NSSetIntValueAndNotify内部实现:

  • 首先调用_NSSetIntValueAndNotify
  • 然后调用父类的setter方法([super setAge:age]);
  • 最后调用didChangeValueForKey,该方法内部回调监听器方法( observeValueForKeyPath:ofObject:change:context:),整个监听过程完成。

_NSSet***ValueAndNotify

上述例子是对 int 属性进行监听,所以对应着_NSSetIntValueAndNotify,不同类型的属性对应不同的_NSSet×××ValueAndNotify方法;

代码验证

在Person类中,重写willChangeValueForKeydidChangeValueForKey,查看执行状态;

@implementation Person
- (void)setAge:(int)age
{
    _age = age;
    
    NSLog(@"setAge:");
}

- (void)willChangeValueForKey:(NSString *)key
{
    [super willChangeValueForKey:key];
    
    NSLog(@"willChangeValueForKey");
}

- (void)didChangeValueForKey:(NSString *)key
{
    NSLog(@"didChangeValueForKey - begin");
    
    [super didChangeValueForKey:key];
    
    NSLog(@"didChangeValueForKey - end");
}
@end
2019-07-15 19:17:29.235721+0800 KVO[5624:7935847] willChangeValueForKey
2019-07-15 19:17:29.235880+0800 KVO[5624:7935847] setAge:
2019-07-15 19:17:29.235975+0800 KVO[5624:7935847] didChangeValueForKey - begin
2019-07-15 19:17:29.236946+0800 KVO[5624:7935847] age ---- {
    kind = 1;
    new = 20;
    old = 10;
}
2019-07-15 19:17:29.237125+0800 KVO[5624:7935847] didChangeValueForKey - end

可以看出监听回调确实是在didChangeValueForKey内部执行的。

源代码demo

相关文章

网友评论

      本文标题:KVO原理

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