美文网首页
iOS KVO KVC

iOS KVO KVC

作者: gaookey | 来源:发表于2021-07-22 11:03 被阅读0次

KVO

基本使用

给 person 对象添加KVO监听

Person *person = [[Person alloc] init];
person.age = 10;
self.person = person;
 
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

当监听对象的属性值发生改变时,就会调用。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

    NSLog(@"属性值更改了 %@", change);
}

移除监听

- (void)dealloc {
    [self.person removeObserver:self forKeyPath:@"age"];
}
本质分析
Person *person1 = [[Person alloc] init];
person1.age = 1;
self.person1 = person1;

Person *person2 = [[Person alloc] init];
person2.age = 2;
self.person2 = person2;

[self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
image.png

person1 使用KVO监听后,person1isa 会指向一个全新的类 NSKVONotifying_PersonNSKVONotifying_Person 是使用 Runtime 动态创建的一个 Person 类的子类,即 NSKVONotifying_Personsuperclass 指向的是 Person 类对象。

  • 如果对实例对象 person2age 进行赋值,person2 会通过 isa 指针找到类 Person,然后找到方法列表中的 setAge 进行调用。
  • 如果对实例对象 person1age 进行赋值, person1 会通过 isa 指针找到类 NSKVONotifying_Person,然后找到方法列表中的 setAge 进行调用。但是 NSKVONotifying_Person 中的 setAge 方法内大致的逻辑是:执行 Foundation 框架中的 _NSSetIntValueAndNotify 函数,而 _NSSetIntValueAndNotify 函数内部会陆续调用 willChangeValueForKey[super setAge:age](原来的setter实现)、didChangeValueForKey 等方法,didChangeValueForKey 方法会执行 observeValueForKeyPath: ofObject: change: context: 通知监听器,最终执行代理方法 observeValueForKeyPath: ofObject: change: context:

因为 ageint 类型,所以是 _NSSetIntValueAndNotify。如果属性 age 是其它类型,相对应的还有 _NSSetBoolValueAndNotify_NSSetCharValueAndNotify_NSSetObjectValueAndNotify_NSSetFloatValueAndNotify_NSSetSizeValueAndNotify、 等。

NSKVONotifying_Person 类中重写了 - (Class)class 方法,使得 [self.person1 class]Person,而通过运行时 object_getClass(self.person1) 获取时是 NSKVONotifying_Person

获取 NSKVONotifying_Person 类中的方法:

Class class = object_getClass(self.person1);
NSMutableArray *methodNames = [NSMutableArray array];
    
unsigned int count;
Method *methodList = class_copyMethodList(class, &count);
for (NSInteger i = 0; i < count; i ++) {
    Method method = methodList[i];
    NSString *methodName = NSStringFromSelector(method_getName(method));
    [methodNames addObject:methodName];
}
free(methodList);
/*
 (
     "setAge:",
     class,
     dealloc,
     "_isKVOA"
 )
 */
NSLog(@"%@", methodNames);
验证
NSLog(@"person1 添加KVO监听之前 类对象(self.person1.isa): %@ 元类对象(self.person1.isa.isa): %@ 方法内存地址: %p", object_getClass(self.person1), object_getClass(object_getClass(self.person1)), [self.person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person2 添加KVO监听之前 类对象(self.person2.isa): %@ 元类对象(self.person2.isa.isa): %@ 方法内存地址: %p", object_getClass(self.person2), object_getClass(object_getClass(self.person2)), [self.person2 methodForSelector:@selector(setAge:)]);

[self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

NSLog(@"person1 添加KVO监听之后 类对象(self.person1.isa): %@ 元类对象(self.person1.isa.isa): %@ 方法内存地址: %p", object_getClass(self.person1), object_getClass(object_getClass(self.person1)), [self.person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person2 添加KVO监听之后 类对象(self.person2.isa): %@ 元类对象(self.person2.isa.isa): %@ 方法内存地址: %p", object_getClass(self.person2), object_getClass(object_getClass(self.person2)), [self.person2 methodForSelector:@selector(setAge:)]);
image.png image.png

所以 person1对象的 setAge: 具体实现是 _NSSetIntValueAndNotify


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

  • 利用 RuntimeAPI 动态生成一个子类,并且让instance对象的isa指向这个全新的子类
  • 当修改instance对象的属性时,会调用 Foundation_NSSetxxxValueAndNotify 函数
    -- willChangeValueForKey:
    -- 父类原来的setter
    -- didChangeValueForKey: 内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:

手动调用 willChangeValueForKey:didChangeValueForKey: 可以手动触发KVO

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

KVC

KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性。

常见的API有:

  • (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
  • (void)setValue:(id)value forKey:(NSString *)key;
  • (id)valueForKeyPath:(NSString *)keyPath;
  • (id)valueForKey:(NSString *)key;
setValue:forKey: 的原理
  1. 首先按照 setKey:_setKey: 的顺序查找方法。如果找到了方法就传递参数,调用方法。

  2. 如果没找到,则查看 accessInstanceVariablesDirectly 方法 的返回值(默认值是 YES),如果返回 NO,调用 setValue: forUndefinedKey: 并抛出异常 NSUnknownKeyException

  3. 如果 accessInstanceVariablesDirectly 返回 YES,按照 _key_isKeykeyisKey 的顺序查找成员变量,找到了成员变量直接赋值。

  4. 如果没找到成员变量,调用 setValue: forUndefinedKey: 并抛出异常 NSUnknownKeyException

valueForKey: 的原理
  1. 首先按照 getKeykeyisKey_key 的顺序查找方法,找到了就调用方法。

  2. 如果没找到,则查看 accessInstanceVariablesDirectly 方法 的返回值(默认值是 YES),如果返回 NO,调用 valueForUndefinedKey: 并抛出异常 NSUnknownKeyException

  3. 如果 accessInstanceVariablesDirectly 返回 YES,按照 _key_isKeykeyisKey 的顺序查找成员变量,找到了成员变量直接取值。

  4. 如果没找到成员变量,调用 valueForUndefinedKey: 并抛出异常 NSUnknownKeyException

相关文章

  • KVC 和 KVO

    iOS-KVC和KVO精炼讲解(干货)KVC 和 KVOiOS开发系列--Objective-C之KVC、KVO细...

  • KVC

    iOS 如何使用KVC iOS开发UI篇—Kvc简单介绍 iOS开发系列--Objective-C之KVC、KVO

  • iOS - KVO

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

  • iOS-KVO浅谈

    上一篇:iOS-KVC浅谈 前言:KVO 作为 KVC 的同袍兄弟,功能更强大,聊聊 KVO。 一、KVO 简介 ...

  • iOS日记15-KVC

    1.iOS开发技巧系列---详解KVC 2.漫谈 KVC 与 KVO 3.KVC/KVO原理详解及编程指南 关键点...

  • iOS面试题:KVC的赋值和取值过程是怎样的?KVO原理是什么?

    更多:iOS面试题大全 1、KVC赋值 2、 KVC取值 3、 KVO原理 KVO 是 Objective-C 对...

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

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

  • 从网上收集的一些关键知识点总结

    iOS 中KVC、KVO、NSNotification、delegate 总结及区别

  • 知识点3

    26. 什么是KVC和KVO? KVO: iOS开发-KVO的奥秘 http://www.jianshu.com/...

  • 无标题文章

    ios中的 kvc和kvo的区别 KVC编程时setValue(value: AnyObje...

网友评论

      本文标题:iOS KVO KVC

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