美文网首页
键路径(keyPath)、键值编码(KVC)、键值观察(KVO)

键路径(keyPath)、键值编码(KVC)、键值观察(KVO)

作者: 青菜白玉堂 | 来源:发表于2021-04-25 10:39 被阅读0次

键路径(keyPath)

keyPath定义
键路径(keyPath)是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一起的对象性质序列。
用法:@"对象.属性"
作用:常见于KVC,KVO编程,可以通过键路径访问到属性的属性。

键值编码(KVC)

KVC定义
KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过Key名直接【访问对象的属性】,或者给对象的【属性赋值】。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。很多高级的iOS开发技巧都是基于KVC实现的。
作用:在.h文件中声明过属性的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用。但是没有在.h文件中声明过属性的的类中,点语法无法使用,这时KVC就有优势了。
使用场景:
动态地取值和设值,
用KVC来访问和修改私有变量,
Model和字典转换,
修改一些控件的内部属性,
用KVC实现高阶消息传递,
操作集合,
实现KVO

注意点
  1. 对不存在的属性,进行kvc赋值,会抛出异常,发生崩溃。
    //kvc-不存在的nameString3
    [self.homeVc setValue:@"张三" forKeyPath:@"nameString3"];

崩溃信息打印:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<homeController 0x7fe60e509ce0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key nameString3.'
解决办法:

在 homeController里重写

//kvc-对不存在的nameString3赋值-默认会抛出异常,引起崩溃,重写forUndefinedKey,则不会抛出异常【可在此处进行拦截,然后重定向赋值】
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{

    NSLog(@"key = %@, value = %@", key, value);

    if([key isEqualToString:@"nameString3"])
    {
          self.nameString2 = value;
          return;
    }

}

2.对不存在的属性,进行kvcf取值,会抛出异常,发生崩溃。NSLog(@"%@",[self.homeVc valueForKey:@"nameString3"]);

崩溃信息打印:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<homeController 0x7fa549c124c0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key nameString3.'

//kvc-对不存在的nameString3取值-默认会抛出异常,引起崩溃,重写valueForUndefinedKey,则不会抛出异常【可在此处进行拦截,然后重定向返回值】
- (nullable id)valueForUndefinedKey:(NSString *)key{
    
    if([key isEqualToString:@"nameString3"])
    {
        return @"不存在的nameString3";
         
    }
    
    return nil;
}
KVC赋值流程
19965398-611d608c0b64e63c.png

setValue:forKey:的默认实现是给定key和value作为参数输入,尝试把value设置给以key命名的属性。过程如下:

按序搜索set<Key>:或_set<Key>,如果找到,则使用输入参数调用并结束。
如果没有找到简单的访问器方法,并且如果类方法accessInstanceVariablesDirectly返回YES(默认为YES),则按序搜索以下实例变量: _<key>/_is<Key>/<key>/is<Key>,如果找到了则直接进行赋值并结束。
以上方法皆失败则调用setValue:forUndefinedKey:,这个方法默认抛出异常,NSObject的子类可以自定义。

KVC取值流程
19965398-14c9ed20aa531ac4.png

valueForKey:的默认实现是,给定key参数作为输入,通过下面的过程,在接收valueForKey:调用的类实例中操作。

1.按顺序搜索访问器方法get<Key> / <key> / is<Key> / _<key>。如果找到,调用该方法并且带着方法的调用结果调转到第5步执行;否则,继续下一步。

2.如果没有找到简单的访问方法,搜索其名称匹配某些模式的方法的实例。其中匹配模式包含countOf<Key>,objectIn<Key>AtIndex:(对应于NSArray定义的基本方法),和<key>AtIndexs:(对应于NSArray的方法objectsAtIndexs:)
一旦找到第一个和其他两个中的至少一个,则创建一个响应所以NSArray方法并返回该方法的集合代理对象。否则,执行第3步。代理对象随后将任何NSArray接收到的一些组合的消息。

3.如果没有找到简单的访问器方法或数组访问方法组,则寻找三个方法countOf<Key>/enumeratorOf<Key>/memberOf<Key>:,对应NSSet类的基本方法。

4.如果三个方法全找到了,则创建一个集合代理对象来响应所有的NSSet方法并返回。否则,执行第4步。
如果上面的方法都没有找到,并且接受者的类方法accessInstanceVariablesDirectly返回YES(默认YES),则按序搜索以下实例变量:_<key> / _is<Key> / <key> / is<Key>。如果找到其中之一,直接获取实例变量的值并跳转到第5步;否则执行第6步。

5.如果检索到的属性值是对象指针,则只返回结果;如果值是受NSNumber支持的标量,则将其存储在NSNumber实例中并返回;如果结果是NSNumber不支持的标量,则转换成NSValue对象并返回

6.如果以上所有的尝试都失败了,则调用valueForUndefinedKey:,这个方法默认抛出异常,NSObject的子类可以重写来自定义行为。

键值观察(KVO)

KVO定义
KVO 即 Key-Value Observing,翻译成键值观察。它是一种观察者模式的衍生。其基本思想是,对目标对象的某属性添加观察,当该属性发生变化时,通过触发观察者对象实现的KVO接口方法,来自动的通知观察者。

观察者模式是什么 一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象。这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的。观察者模式较完美地将目标对象与观察者对象解耦。

作用:简单来说KVO可以通过监听key,来获得value的变化,用来在对象之间监听状态变化。KVO的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueObserving类别名,所以对于所有继承了NSObject的类型,都能使用KVO(一些纯Swift类和结构体是不支持KVC的,因为没有继承NSObject)。

原理

KVO的实现依赖于Runtime的强大动态能力。
即当一个类型为 ObjectA 的对象,被添加了观察后,系统会生成一个 NSKVONotifying_ObjectA 类,并将对象的isa指针指向新的类,也就是说这个对象的类型发生了变化。这个类相比较于ObjectA,会重写以下几个方法。
重写setter
重写class
重写dealloc
重写_isKVOA

参考:
kvc,kvo
https://juejin.im/post/6844903602545229831
https://juejin.im/post/6844903705729302541#heading-1
kvc
https://www.jianshu.com/p/70ba4f598b49
https://www.jianshu.com/p/1d39bc610a5b
kvc崩溃
https://blog.csdn.net/u010259906/article/details/50379254
kvo
https://www.jianshu.com/p/badf5cac0130

相关文章

网友评论

      本文标题:键路径(keyPath)、键值编码(KVC)、键值观察(KVO)

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