美文网首页
iOS Runtime之KVO

iOS Runtime之KVO

作者: 谢二九 | 来源:发表于2022-06-26 08:58 被阅读0次

Runtime系列导读

KVO简介

全称Key-Value Observing,KVO是Object-C中定义的一个通知机制,其定义了一种对象间监控对方状态的改变,并做出反应的机制。对象可以为自己的属性注册观察者,当这个属性的值发生了改变,系统会对这些注册的观察者做出通知。

KVO用法

添加监听

- (void)addObserver:(NSObject *)observer  
         forKeyPath:(NSString *)keyPath  
            options:(NSKeyValueObservingOptions)options  
            context:(void *)context  
  • observer: 观察者对象. 其必须实现方法observeValueForKeyPath:ofObject:change:context:
  • keyPath: 被观察的属性,不能为nil
  • options: 设定通知观察者时传递的属性值的类型,具体设置可查看枚举 NSKeyValueObservingOptions
  • context: 一些其他的需要传递给观察者的上下文信息,通常设置为nil

监听实现

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                      context:(void *)context
  • keyPath: 被观察的属性
  • object: 被观察的对象
  • change: 根据options设置,可能出现old|new,或者都有
  • context: 监听时传入的上下文信息

KVO实现过程

KVO的实现过程实际上是利用了OC的runtime机制,当一个实例对象添加观察者时,底层根据该实例对象所属的类动态添加了一个类(动态添加的类名就是在原来类的类名前加上NSKVONotifying_前缀),这个类是继承自原来的类的。这里以继承自NSObject的KVOTest类来举例。

  • KVOTest实现:
@interface KVOTest : NSObject

@property (nonatomic, assign) NSInteger age;


@end

@implementation KVOTest

-(void)didChangeValueForKey:(NSString *)key
{
    [super didChangeValueForKey:key];
    NSLog(@"didChangeValueForKey:%@,%p", key, self);
}

@end
  • 调用代码:
-(void)testKVO2
{
    self.test = [KVOTest new];
    self.test.age = 10;
    NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.test addObserver:self forKeyPath:@"age" options:option context:nil];
    self.test.age = 10;
}
  • 监听代码:
-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                      context:(void *)context
{
    NSLog(@"%@ - %@" , keyPath, change);
}
  • 打印日志:
2022-06-25 21:58:32.716895+0800 StudyApp[31571:1344036] age - {
    kind = 1;
    new = 10;
    old = 10;
}
2022-06-25 21:58:32.716960+0800 StudyApp[31571:1344036] didChangeValueForKey:age,0x600003f14590

上面实例的底层实现过程如下:

  • self.test添加观察者时,底层就利用runtime动态生成一个叫NSKVONotifying_KVOTest的类,这个类继承自KVOTest类,并重写了以下实例方法:
    • 重写class方法,不重写的话调用这个方法返回的是NSKVONotifying_KVOTest这个类,重写后返回的是原本的KVOTest类。苹果这么做的目的是为了隐藏KVO的实现细节。
    • 重写dealloc方法,在这个方法里面做一些收尾的工作。
    • 重写_isKVOA方法,这是一个私有方法,我们不必关心。
    • 重写被监听属性的setter方法,上面案例只监听了name属性,所以只需重写setName:方法。重写setter是实现KVO的关键,在setter方法里面实际是调用的Foundation框架下的_NSSetValueAndNotify方法(表示不是一个固定的,这个和监听的属性的类型有关,比如是属性是int类型的话这里就是__NSSetIntValueAndNotify,所包含的类型会在后面列出来)。
  • 然后将self.test这个实例对象的isa改为指向NSKVONotifying_KVOTest(原本是指向KVOTest类的)。
  • 当我们设置被监听属性的值时self.test.age = 10,是调用的setAge:方法,前面说了setAge:方法被重写了,所以实际上调用的是_NSSetIntValueAndNotify这个方法。这个方法实现苹果是没有开源的,无法得知其具体实现,不过可以猜出其实现流程大致如下:
    • 首先调用[self willChangeValueForKey:@"age"];这个方法。
    • 然后调用原先的setter方法的实现(比如_age = age;);
    • 再调用[self didChangeValueForKey:@"age"];这个方法。
    • 最后在didChangeValueForKey:这个方法中调用观察者的observeValueForKeyPath: ofObject: change: context:方法来通知观察者属性值发生了变化。

KVO答疑

如何手动触发KVO?

  • 手动调用willChangeValueForKey: 和 didChangeValueForKey:

直接修改成员变量会触发KVO吗?

  • 不会触发KVO

对同一个属性N次注册,修改一次该属性,observeValueForKeyPath会调用几次

N次。

对同一个属性一次注册,多次removeObserver,会发生什么

crash,提示**'Cannot remove an observer <ViewController 0x7fa3fd708c60> for the key path "age" from <KVOTest 0x6000018e8790> because it is not registered as an observer.'**

相关文章

  • iOS Runtime学习笔记 (二) - 实战应用

    iOS runtime实战应用 iOS runtime 进行添加属性,并支持KVO监听 iOS 中category...

  • iOS Runtime之KVO

    Runtime系列导读 iOS Runtime之方法查找[https://www.jianshu.com/p/f6...

  • iOS runtime(三)runtime之method(1)m

    iOS runtime(一)runtime之Property 详尽iOS runtime(二)runtime之Iv...

  • iOS 自定义KVO

    自己实现kvo之前,需要知道iOS系统对kvo的实现。 系统实现kvo的原理 这依赖了OC强大的runtime特性...

  • KVO 的本质?

    iOS 用什么方式实现对一个对象的 KVO ? (KVO 的本质是什么?) 首先利用 Runtime API 动态...

  • KVO应用、原理及自实现

    一.KVO简介 KVO 是ios里,观察者设计模式的一种应用实现,依赖runtime,基于KVC,KVO提供了一种...

  • Runtime

    kyson老师 iOS开发之runtime(1):runtime调试环境搭建iOS开发之runtime(2):浅析...

  • iOS开发之Runtime常用示例总结

    iOS开发之Runtime常用示例总结 iOS开发之Runtime常用示例总结

  • 这些个技术要求

    iOS下的网络通信机制, 内存管理机制, GCD, Block, KVC, KVO, Runtime, Runlo...

  • iOS_KVO本质解析

    iOS 用什么方式实现对一个对象的KVO?(KVO的本质是什么) 利用Runtime API动态生成一个子类, 并...

网友评论

      本文标题:iOS Runtime之KVO

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