KVC
的全拼是Key-Value Coding
,中文是键值编码
。是由NSKeyValueCoding
非正式协议的一种机制。对象可以间接地访问它们的属性。这种间接访问机制是实例变量及其相关访问器方法提供的直接访问的补充。
使用KVC
通过key
取值和设置值
1 | //直接通过Key来取值 |
通过keyPath
(路由)取值和设置值
1 | //通过KeyPath来取值 |
我们平时项目中主要是通过使用的是valueForKey
和valueForKeyPath
的方式取值和设值,当然还有集合类型
的一些操作可以参见苹果KVC的文档查看使用。
KVC
的设值过程
setValue:forKey:
这个方法有一个调用顺序(基本数据类型):
- 按顺序查找名为 set
: 或 _set 的第一个访问器。如果找到,则使用输入值(或根据需要展开的值)调用它并完成。 - 如果未找到简单访问器,并且类方法 accessInstanceVariablesDirectly 返回 YES,则按顺序查找名称类似于 _
、_is 、 或 is 的实例变量。如果找到,直接使用输入值(或解包值)设置变量并完成。 - 在未找到访问器或实例变量时,调用 setValue:forUndefinedKey:。默认情况下,这会引发异常,但 NSObject 的子类可能会提供特定于键的行为。
整个流程图如下(以person
对象设置name
属性为例):
KVC
取值过程
和设值过程一样,取值过程valueForKey:
也有一个调用顺序(包含集合类型):
- 在实例中搜索找到的第一个访问器方法,其名称类似于 get
、 、is 或 _ ,按该顺序。如果找到,则调用它并使用结果继续执行步骤 5。否则继续下一个步骤 - 如果没有找到简单的访问器方法,则在实例中搜索名称与模式 countOf
和 objectIn AtIndex:(对应于 NSArray 类定义的原始方法)和 AtIndexes:(对应于模式)的方法NSArray 方法 objectsAtIndexes:)。如果找到其中的第一个和至少其他两个中的一个,则创建一个集合代理对象,该对象响应所有 NSArray 方法并返回该对象。否则,继续执行步骤 3。代理对象随后将它接收到的任何 NSArray 消息转换为 countOf 、objectIn AtIndex: 和 AtIndexes: 消息的某种组合,并将其转换为创建它的键值编码兼容对象。如果原始对象还实现了一个可选方法,其名称类似于 get :range:,则代理对象也会在适当的时候使用它。实际上,代理对象与键值编码兼容对象一起工作允许底层属性表现得好像它是一个 NSArray,即使它不是。 - 如果没有找到简单的访问器方法或数组访问方法组,则查找名为 countOf
、enumeratorOf 和 memberOf 的三元组方法:(对应于 NSSet 类定义的原始方法)。如果找到所有三个方法,则创建一个集合代理对象,该对象响应所有 NSSet 方法并返回该对象。否则,继续执行步骤 4。这个代理对象随后将它接收到的任何 NSSet 消息转换为 countOf 、enumeratorOf 和 memberOf 的某种组合:消息到创建它的对象。实际上,与键值编码兼容的对象一起工作的代理对象允许底层属性表现得好像它是一个 NSSet,即使它不是。 - 如果没有找到简单的访问器方法或集合访问方法组,并且如果接收者的类方法accessInstanceVariables直接返回YES,则搜索名为_
、_is 、 或is 的实例变量,以该顺序。如果找到,直接获取实例变量的值并进行步骤5,否则进行步骤6。 - 如果检索到的属性值是一个对象指针,只需返回结果即可。 如果该值是 NSNumber 支持的标量类型,则将其存储在 NSNumber 实例中并返回。 如果结果是 NSNumber 不支持的标量类型,则转换为 NSValue 对象并返回。
- 如果所有其他方法都失败,请调用 valueForUndefinedKey:。默认情况下,这会引发异常,但 NSObject 的子类可能会提供特定于键的行为。
其流程图如下:
自定义实现KVC
如果自己实现一个KVC
可以参考上面的顺序,实现valueForKey
和setValueForKey
。
设值过程
- 判断
key
是否为空,为空直接返回。 - 查找是否有 setter方法
set<Key>:
,_set<Key>
,setIs<Key>
,如果有则实现并返回。 - 如果没找到则判断accessInstanceVariablesDirectly的返回值是否为YES,可以则往下走,否则抛出异常。
- 查找自己的ivar列表中是否包含实例变量
_<key>
,_is<Key>
,<key>
,is<Key>
,找到就赋值。 - 如果都搜索不到,就抛出异常。
实现代码
1 | - (void)js_setValue:(nullable id)value forKey:(NSString *)key{ |
取值过程
- 同样是判断key非空
- 按顺序查找方法:
get<Key>
、<key>
、countOf<Key>
、objectIn<Key>AtIndex
- 判断accessInstanceVariablesDirectly的返回值是否为YES,可以则往下走,否则抛出异常。
- 查找自己的ivar列表中是否包含实例变量
_<key>
,_is<Key>
,<key>
,is<Key>
,找到就取值。 - 未找到抛出异常。
1 | - (nullable id)js_valueForKey:(NSString *)key{ |
总结
本篇我们主要探索了KVC的取值
和设值
的流程。设值过程集合
类型的情况没有写,感兴趣的童鞋可以查看苹果官方文档进行探索。