美文网首页
iOS KVO原理探索

iOS KVO原理探索

作者: Devbrave | 来源:发表于2019-06-12 17:35 被阅读0次

kvo全称Key-Value-Observing,俗称“键值监听”,可以用于对对象的某个属性值的监听。kvoObjective-C对观察者模式的实现。由于KVO的实现机制,一般继承自NSObject的对象都默认支持KVO

具体的使用方法

  • 添加观察者的方法
     [_scrollView addObserver:self
                 forKeyPath:@"contentOffset"
                    options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
                    context:nil];

self:监听对象,keyPath:被监听的属性,option:需要监听的属性的选项,context:上下文、用于传递数据;

  • 实现监听的方法
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"contentOffset"] && object == _scrollView) {
        id newValue = change[NSKeyValueChangeNewKey];
        id oldValue = change[NSKeyValueChangeOldKey];
        NSLog(@"object:%@,\n keyPath: %@,\n newValue:%@,\n oldValue:%@,\n context:%@", object, keyPath, newValue, oldValue, context);
    }
}

keyPath:被监听的属性,object:被监听的对象,change:属性值改变的状况。(存储着属性的新值和旧值等),context:上下文传递的值;

  • 移除观察者,在不需要监听的时候一定要移除观察者,通常是在dealloc中
- (void)dealloc {
    [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
}

KVO内部原理实现

我们可以先看一下官方文档怎么说的:

Automatic key-value observing is implemented using a technique called isa-swizzling.
key-value自动监听(即kvo)用了名为isa-swizzling的技术
The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
isa指针指向一个对象的类,这个类包含了一个方法派发表
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
被观察对象的isa指针会被修改,指向一个中间类,而非真实的类.isa指针的值并不必须反应实例对象的真实对应的类
You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.
因而,应该调用对象的class方法来确定实例对应的类而不是isa指针的值

从官方文档我们了解到KVO的实现使用了isa-swizzling的技术,在运行的时候被观察对象的isa指针会被修改,指向一个中间类,而不是真实的类。isa指针的值不会反应实例对象真实对应的类。并且将class方法重写,返回原类的Class。所以苹果建议在开发中不应该依赖isa指针,而是通过class实例方法来获取对象类型。
下面我们来实际探究一下KVOisa指针指向的中间内是什么?
我们监听了UIScrollViewcontentOffset属性:

[_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

在添加观察者的下一行添加断点调试可以看到控制台的打印:
添加观察者之前


添加观察者之前截图.png

添加观察者之后


添加观察者之后.png
//添加观察者之前
(lldb) po _scrollView->isa
UIScrollView
//添加观察者之后
(lldb) po _scrollView->isa
NSKVONotifying_UIScrollView

从打印的信息中我们可以看到在添加观察者之前isa指针指向的是真实的类UIScrollView,添加观察者之后_scrollView对象的isa指针指向了NSKVONotifying_UIScrollView。我们可以得出结论:在对象添加了观察者之后对象的isa指针会指向NSKVONotifying_A的分类。

isa指针指向中间类之后了,中间类是怎么告诉我们被观察属性值的变化的了。

现在我们在VC中来观察属性n,并在点击方法中改变n的值。

@property (nonatomic, assign) NSInteger n;

    [self addObserver:self
           forKeyPath:@"n"
              options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
              context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
     if ([keyPath isEqualToString:@"n"]) {
        NSInteger nwe = [change[NSKeyValueChangeNewKey] integerValue];
        NSLog(@" ========== n: %ld ==========\n", nwe);
    }
}

- (void)tapAction {
    self.n += 1;
   _n += 1;
}
//打印如下:
========== n: 1 ==========

当我们以点方法赋值n的时候会出发观察者方法,可以看到值的改变。但是当我们以下划线的方式改变n的值时,可以发现观察者方法并没有触发。我们知道self.n其实是调用nsetter方法。因为被观察对象的isa指针指向了NSKVONotifying_A的中间类,所以调用的setter方法会从NSKVONotifying_A中找。可知NSKVONotifying_A重新实现了setter方法。
从官方文档中我们可以知道,有两种方法可以确保被观察的属性被通知发射:

There are two techniques for ensuring the change notifications are emitted.

  1. 自动改变通知即实现+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key,决定是否对被观察的属性自动通知。
  2. 手动改变通知,在setter中,赋值前后主动调用willChangeValueForKeydidChangeValueForKey
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    BOOL isAutomatic = NO;
    if ([key isEqualToString:@"n"]) {
        isAutomatic = NO;
    }else {
        isAutomatic = [super automaticallyNotifiesObserversForKey:key];
    }
    return isAutomatic;
}

当对n属性返回NO时,可以看到n的变化并不会触发观察者通知方法。

- (void)setN:(NSInteger)n {
    [self willChangeValueForKey:@"n"];
    _n = n;
    [self didChangeValueForKey:@"n"];
}

当我们手动去调用willChangeValueForKeydidChangeValueForKey时,可以看到n的变化会触发观察者通知方法。

总结

从以上的观察我们可以得知KVO的实现原理:给一个实例对象添加观察者时,会生成该类的一个中间类NSKVONotifying_A,该类会重写被观察属性的setter方法并在赋值前后调用willChangeValueForKeydidChangeValueForKey实现被观察者值变化时的通知。

相关文章

  • iOS KVO原理探索

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

  • KVO学习笔记

    1.KVO初探学习2.KVO 底层原理探索 1.KVO初探学习 移除观察者的重要性 (IOS11之后说不移除是不对...

  • iOS - KVO

    [toc] 参考 KVO KVC 【 iOS--KVO的实现原理与具体应用 】 【 IOS-详解KVO底层实现 】...

  • Objective-C的本质(4)—— KVO本质

    参考:iOS底层原理总结 - 探寻KVO本质iOS-KVO本质 问题一:kvo如果找到对应的属性 KVO不存在查找...

  • iOS 探索KVO底层原理

    KVO底层原理 记 上一篇文章中说到,KVO监听成员变量无法收到回调。先验证一下是不是对的。 1.创建一个Pers...

  • iOS KVO底层原理探索

    一,KVO (Key-Value Observing) KVO是Objective-C对观察者设计模式的一种实现,...

  • iOS探索KVO实现原理,重写KVO

    写响应式编程博客时,提到了KVO,今天我们探索一下KVO的实现原理及如何自己实现KVO功能 首先简单的KVO实现 ...

  • iOS原理(二)----KVO,KVC

    iOS原理(二)----KVO,KVC KVO KVO的全称是Key-Value Observing,俗称“键值监...

  • iOS底层--KVO(一)-基础知识点

    KVO: 全称--- Key-value observing 探索KVO原理和KVO一样,通过官方文档去看。 KV...

  • IOS KVO原理解析与应用

    IOS KVO原理解析与应用 一、KVO概述 KVO,即:Key-Value Observing,是Objecti...

网友评论

      本文标题:iOS KVO原理探索

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