美文网首页
iOS用什么方式实现一个对象的KVO?

iOS用什么方式实现一个对象的KVO?

作者: buding_ | 来源:发表于2024-03-21 16:47 被阅读0次
KVO: key-value observing(键值监听),可用于监听某个对象属性值的改变
 添加监听:-addObserver:forKeyPath:options:context
 接收监听:-observeValueForKeyPath:ofObject:change:context:
首先我们来测试一下
@interface Person: NSObject { 
    @property(assign) int age; 
}
@end

@implementation ViewController
- (void)viewDidLoad {
    self.person1 = [[Person alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[Person alloc] init];
    self.person2.age = 2;

    NSLog(@"person1添加KVO监听之前 - %@(%p) %@(%p)",
          object_getClass(self.person1),self.person1,
          object_getClass(self.person2),self.person2);
    NSLog(@"person1添加KVO监听之前 - %p %p",
          [self.person1 methodForSelector:@selector(setAge:)],
          [self.person2 methodForSelector:@selector(setAge:)]);
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    
    NSLog(@"person1添加KVO监听之后 - %@(%p) %@(%p)",
          object_getClass(self.person1),self.person1,
          object_getClass(self.person2),self.person2);
    NSLog(@"person1添加KVO监听之后 - %p %p",
          [self.person1 methodForSelector:@selector(setAge:)],
          [self.person2 methodForSelector:@selector(setAge:)]);
    NSLog(@"类对象 - %@ %@",
          object_getClass(self.person1),  // self.person1.isa
          object_getClass(self.person2)); // self.person2.isa
  }
@end

log:
person1添加KVO监听之前 - Person Person
person1添加KVO监听之前 - 0x1003ce11c 0x1003ce11c
person1添加KVO监听之后 - NSKVONotifying_Person Person
person1添加KVO监听之后 - 0x180b2bd00 0x1003ce11c
类对象 - NSKVONotifying_Person Person

通过LLDB,p (IMP)0x180b2bd00得到:
(IMP) $1 = 0x0000000180b2bd00 (Foundation`_NSSetIntValueAndNotify)

通过log我们可以看到一个很奇怪的现象,添加监听之后person1的class居然变为了一个名为“NSKVONotifying_Person”

然后我们尝试提前自己声明一个类: NSKVONotifying_Person, 这时再执行上面的代码会报错:KVO failed to allocate class pair for name NSKVONotifying_MJPerson, automatic key-value observing will not work for this class

那么要实现一个对象的KVO就明显跟这个NSKVONotifying_Person密切相关了,我们进一步看下这个NSKVONotifying_Person class,我们通过runtime打印这个NSKVONotifying_Person的内部方法看看

@implementation ViewController
- (void)printMethodNamesOfClass:(Class)cls
{
    unsigned int count;
    // 获得方法数组
    Method *methodList = class_copyMethodList(cls, &count);
    // 存储方法名
    NSMutableString *methodNames = [NSMutableString string];
    // 遍历所有的方法
    for (int i = 0; i < count; i++) {
        // 获得方法
        Method method = methodList[i];
        // 获得方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        // 拼接方法名
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    // 释放
    free(methodList);
    // 打印方法名
    NSLog(@"%@ %@", cls, methodNames);
}

- (void)viewDidLoad {
    self.person1 = [[Person alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[Person alloc] init];
    self.person2.age = 2;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    
    [self printMethodNamesOfClass:object_getClass(self.person1)];
    [self printMethodNamesOfClass:object_getClass(self.person2)];
}
@end

log:
NSKVONotifying_Person setAge:, class, dealloc, _isKVOA,
Person setAge:, age,

NSKVONotifying_Person重写了Person的setAge:, class, dealloc方法 ;
而重写后的setAge方法调取了Foundation框架中的_NSSetIntValueAndNotify函数;
那么我们总结一下,

在添加监听后,对象发生了什么事?(如何实现KVO? & KVO的本质是什么?)
  • 在添加监听后,通过runtime API,创建NSKVONotifying_Person: Person, 且更改person1对象的isa指向NSKVONotifying_Person;
  • NSKVONotifying_Person中重写该属性的-setAge:方法为Founddation框架中的函数:_NSSetIntValueAndNotify;
  • _NSSetIntValueAndNotify中大概做了:
    [self willChangeValueForKey:@"age"];
    [super setAge:_age];
    [self didChangeValueForKey:@"age"];
    而didChangeValueForKey中触发observeValueForKeyPath:ofObject:change:context:

如此便实现了KVO

如何手动触发KVO?

添加监听方法后,手动调用willChangeValueForKey和didChangeValueForKey,即可触发

取消监听后,可以发现person1的isa重新指向了Persion class

相关文章

  • KVO

    iOS用什么方式实现对一个对象的KVO?(KVO的本质) 1 未使用KVO监听的对象 MJPerson insta...

  • 底层原理总结 — KVO

    1、iOS用什么方式实现一个对象的KVO?(KVO的本质是什么) 比如给Person对象的age属性添加KVO监听...

  • KVO

    KVO (Key-value-observing) 键值监听 iOS用什么方式实现对一个对象的KVO?(KVO的本...

  • KVO 的本质?

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

  • IOS基础知识-KVO原理篇

    问题 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)如何手动触发KVO KVO的全称 Key-V...

  • iOS底层原理汇 - 探索KVO本质

    问题 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 如何手动触发KVO ? 首先需要了解KVO...

  • KVO和KVC的本质

    一、KVO 问题 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 如何手动触发KVO? 1. K...

  • iOS-KVO、KVC

    KVO面试题 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 利用RuntimeAPI动态生成一...

  • KVO底层探索

    问题 1.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?) 答. 当一个对象使用了KVO监听,iO...

  • 通过面试题来剖析KVO以及KVC

    iOS用什么方式实现对一个对象的KVO?(KVO的本质?) NSKVONotifying_MJPerson是使用R...

网友评论

      本文标题:iOS用什么方式实现一个对象的KVO?

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