美文网首页
KVC 和 KVO,属性关键字

KVC 和 KVO,属性关键字

作者: 如果大雨落下 | 来源:发表于2020-06-28 18:58 被阅读0次

kvo,观察者模式

  • 当给一个类添加观察者之后,系统会动态生成这个类的子类:
    比如: Dog类,增加其name属性的监听,动态生成的类名为 NSKVONotifying_Dog, 并且重写子类的方法:-setName,class,dealloc,新增的还有一个方法_iskvo(不用管),

  • 重写class方法,是为了隐藏这个动态生成的子类,因为调用【Dog class】,正常会去返回真实类名,重写class,返回父类名,即Dog,能隐藏动态生成的子类

  • delloc是用于释放

  • 重写setname方法,实则是实现了foundation 框架的 __NSSetIntValueAndNotify方法,里面的Int啥的,根据具体属性去修改,
    __NSSetIntValueAndNotify方法里面,先调用 [self willChangeValueForKey:@"name"];,然后调用父类的setname方法,最后调用 [self didChangeValueForKey:@"name"];,[self didChangeValueForKey:@"name"];里面会调用observeValueForKeyPath: ofObject: change: context:,来告知观察者,属性的变化

总结kvo

KVO的核心是动态生成一个继承自原类的类,然后将实例对象的isa指向这个类。然后重写了监听属性的setter方法,在原有setter方法的前面调用willChangeValueForKey方法,在原有setter方法的后面调用didChangeValueForKey。

所以我们要判断某个操作是否会触发KVO关键在于它是否调用了监听属性的setter方法。比如上面的例子,self.stu1.name = @"Jack";这种方式就是调用setter方法,所以它会触发KVO。但是下面这几种方式是不会触发KVO的:

采用给成员变量赋值的方式,self.stu1->_name = @"Jack";(前提是需要将成员变量_name给暴露出去才能在外面访问),这种方式是不会触发KVO的,因为它没有调用setter方法。
对于集合类型,集合里面数据的更新是不会触发KVO的。比如[self.stu1.booksArr addObject:@"book1"]这样的操作,它同样没有调用setBooksArr:方法,所以不会触发KVO。
如果所监听的属性是一个自定义的OC对象,比如有个Dog类里面有个age属性,Student类里面有个Dog类型的属性dog,如果我们监听dog这个属性,当dog的age发生变化时并不会触发KVO,因为它不会调用setDog:方法。

上面这几种情况,如果我们也想触发KVO的话,我们可以手动触发,也就是在原有方法的前面和后面分别加上willChangeValueForKey和didChangeValueForKey这两个方法。就比如最后这个例子,我们可以这样写:

[self.stu1 willChangeValueForKey:@"dog"];
self.stu1.dog.age = 3;
[self.stu1 didChangeValueForKey:@"dog"];

kvc 键值编码

  • setValue:forKey:方法:给模型的属性赋值

赋值原理:
(1)去模型中查找有没有setIcon方法,有就直接调用这个set方法,给模型这个属性赋值[self setIcon:dict[@"icon"]];
(2)如果找不到icon属性,接着又会去寻找_icon属性,如果有,直接_icon = dict[@"icon"];
(3)如果找不到set方法,接着就会去寻找有没有icon属性,如果有,就直接访问模型中icon = dict[@"icon"];
(4)如果都找不到就会报错
[<Flag 0x7fb74bc7a2c0> setValue:forUndefinedKey:]

图片.png

当我们设置setValue:forKey:时

首先会查找setKey:、_setKey: (按顺序查找)

如果有直接调用

如果没有,先查看accessInstanceVariablesDirectly方法

如果可以访问会按照 _key、_isKey、key、iskey的顺序查找成员变量

找到直接复制

未找到报错NSUnkonwKeyException错误

  • valueforkey:


    图片.png

    get的搜索规则
    get的搜索规则相对于set就有点复杂了

    1.通过getter方法搜索实例,例如get<Key>, <key>, is<Key>, <key>的拼接方案。按照这个顺序,如果发现符合的方法,就调用对应的方法并拿着结果跳转到第五步。否则,就继续到下一步。
    2.如果没有找到简单的getter方法,则搜索其匹配模式的方法countOf<Key>、objectIn<Key>AtIndex:、<key>AtIndexes:。如果找到其中的第一个和其他两个中的一个,则创建一个集合代理对象NSKeyValueArray,该对象响应所有NSArray的方法并返回该对象。否则,继续到第三步。代理对象随后将NSArray接收到的countOf<Key>、objectIn<Key>AtIndex:、<key>AtIndexes:的消息给符合KVC规则的调用方。当代理对象和KVC调用方通过上面方法一起工作时,就会允许其行为类似于NSArray一样。
    3.如果没有找到NSArray简单存取方法,或者NSArray存取方法组。则查找有没有countOf<Key>、enumeratorOf<Key>、memberOf<Key>:命名的方法。如果找到三个方法,则创建一个集合代理对象,该对象响应所有NSSet方法并返回。否则,继续执行第四步。此代理对象随后转换countOf<Key>、enumeratorOf<Key>、memberOf<Key>:方法调用到创建它的对象上。实际上,这个代理对象和NSSet一起工作,使得其表象上看起来是NSSet。
    4.如果没有发现简单getter方法,或集合存取方法组,以及接收类方法accessInstanceVariablesDirectly是返回YES的。搜索一个名为
    <key>、_is<Key>、<key>、is<Key>的实例,根据他们的顺序。如果发现对应的实例,则立刻获得实例可用的值并跳转到第五步,否则,跳转到第六步。
    5.如果取回的是一个对象指针,则直接返回这个结果。如果取回的是一个基础数据类型,但是这个基础数据类型是被NSNumber支持的,则存储为NSNumber并返回。如果取回的是一个不支持NSNumber的基础数据类型,则通过NSValue进行存储并返回。
    6.如果所有情况都失败,则调用valueForUndefinedKey:方法并抛出异常,这是默认行为。但是子类可以重写此方法。

其中第二步搜索的意思是:没有找到第一步中的简单getter方法,但是实现了countOf<Key>以及objectIn<Key>AtIndex:、<key>AtIndexes:两个中的其中一个,此时意味着当前对象拥有一个属性名为<key>的NSKeyValueArray类型的属性,它可以响应NSArray的所有方法。到这里其实就可以回答上面提到的第二个问题,一个对象不一定需要显式的写出自己的属性也可以进行存取操作!
第三步搜索的意思和第二步相似,只是条件更苛刻,且最终返回的是NSSet对象,响应NSSet的所有方法。

可变的搜索

以NSMutableArray为例说明:

  • (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

搜索规则如下:

  1. 搜索insertObject:in<Key>AtIndex: , removeObjectFrom<Key>AtIndex: 或者 insert<Key>AtIndexes , remove<Key>AtIndexes 格式的方法。如果至少找到一个insert方法和一个remove方法,那么同样返回一个可以响应NSMutableArray所有方法代理集合(类名是NSKeyValueFastMutableArray2),那么给这个代理集合发送NSMutableArray的方法,以insertObject:in<Key>AtIndex: , removeObjectFrom<Key>AtIndex: 或者 insert<Key>AtIndexes , remove<Key>AtIndexes组合的形式调用。还有两个可选实现的接口:replaceObjectAtIndex:withObject:,replace<Key>AtIndexes:with<Key>:。
    1. 如果上面的方法没有找到,则搜索set<Key>: 格式的方法,如果找到,那么发送给代理集合的NSMutableArray最终都会调用set<Key>:方法。 也就是说,mutableArrayValueForKey:取出的代理集合修改后,用set<Key>: 重新赋值回去去。这样做效率会低很多。所以推荐实现上面的方法。
    2. 如果上一步的方法还还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),会按_<key>,<key>,的顺序搜索成员变量名,如果找到,那么发送的NSMutableArray消息方法直接交给这个成员变量处理。
      4.如果还是找不到,则调用valueForUndefinedKey:。

上面调用的方法- (NSMutableArray )mutableArrayValueForKey:(NSString )key;其实有一个很重要的使用场景:KVO监听可变集合的变化**,当然这里的可变集合包括NSMutableArray,NSMutableSet,NSMutableOrderedSet,但不包括NSDictionary

https://blog.csdn.net/qq_27909209/article/details/81106631

https://www.jianshu.com/p/7c818767344b

相关文章

  • KVC 和 KVO,属性关键字

    kvo,观察者模式 当给一个类添加观察者之后,系统会动态生成这个类的子类:比如: Dog类,增加其name属性的监...

  • Swift如何使用KVC/KVO

    // KVC/KVO// Swift支持KVC\KVO的条件// 属性所在的类、监听器最终继承自NSObject/...

  • KVC,KVO

    KVC , KVO KVC和KVO的区别及应用 KVC/KVO原理 1. KVC键值编码 KVC,即是指NSKey...

  • KVC、KVO

    KVC、KVO探识(一)KVO和KVO的详细使用 KVC、KVO探识(二)KVC你不知道的东西 KVC、KVO探识...

  • KVC-KVO

    什么是 KVO 和 KVC KVC:键-值 编码 使用字符串直接访问对象属性 KVO:键值观察机制,它提供了观察某...

  • 手动设定实例变量的KVO

    前情提要:KVC 和KVO的keyPath一定是属性?:NO,KVC 默认支持实例变量。一个对象设定为属性,是默认...

  • ios基础——KVO、KVC

    KVO和KVC常见问题: 1.KVC和KVO是什么.2.KVC和KVO的原理是什么3.KVC和KVO的使用场景4....

  • iOS-底层原理(5)-KVC底层原理详解

    面试题 1. 通过KVC修改属性会触发KVO么? 会触发KVO 代码例子佐证 运行结果 2. KVC的赋值和取值过...

  • 底层4:KVC

    面试题:通过KVC修改的属性会触发KVO吗? 会触发KVO。 面试题:KVC的赋值和取值过程是怎样的?原理是什么?...

  • 可能碰到的iOS笔试面试题(7)--KVO-KVC

    KVC-KVO KVC的底层实现? KVO的底层实现? 什么是KVO和KVC? KVO的缺陷? KVO是一个对象能...

网友评论

      本文标题:KVC 和 KVO,属性关键字

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