在OC中,对于属性的访问,setter 和 getter 方法是直接的方式,而KVC(键值编码)则提供了间接访问的API。
KVC(键值编码)是由NSKeyValueCoding非正式协议启用的一种机制,对象采用该协议来提供对其属性的间接访问。当对象符合键-值编码时,它的属性可以通过简洁、统一的消息传递接口通过字符串参数寻址。这种间接访问机制补充了实例变量及其相关访问器方法提供的直接访问。(Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol that objects adopt to provide indirect access to their properties. When an object is key-value coding compliant, its properties are addressable via string parameters through a concise, uniform messaging interface. This indirect access mechanism supplements the direct access afforded by instance variables and their associated accessor methods.)
NSKeyValueCoding在源码中的定义如下:
1 | /* |
从定义可以看出,说是一组协议,事实上,它是一组分类,为NSObject类和集合类扩展并实现了KVC(键值编码)相关的API。
Getter的搜索模式(Search Pattern for the Basic Getter)
给定一个key形参作为输入,valueForKey:的默认实现执行以下过程:
首先在实例中搜索名称为get
<Key>
,<Key>
, is<Key>
,或_<Key>
的访问器方法,按此顺序。如果找到,调用它并带着结果进行步骤5。否则进行下一步;如果没有找到简单的访问器方法,在实例中搜索其名称匹配模式countOf
<Key>
和objectIn<Key>
AtIndex:(对应NSArray类定义的原始方法)和<Key>
AtIndexes:(对应NSArray方法objectsAtIndexes:)的方法。如果找到了其中的第一个和其他两个中的至少一个,创建一个集合代理对象来响应所有NSArray方法并返回该对象。否则,请执行步骤3。
代理对象随后将它接收到的任何NSArray消息转换为countOf
<Key>
、objectIn<Key>
AtIndex:和<Key>
AtIndexes:消息的某种组合,并将这些消息转换为创建它的键值编码兼容的对象。如果原始对象还实现了一个名为get<Key>
:range:的可选方法,代理对象也会在适当的时候使用该方法。实际上,代理对象与键值编码兼容的对象一起工作,允许底层属性的行为就像NSArray一样,即使它不是。如果没有找到简单的访问器方法或数组访问方法组,则查找名为countOf
<Key>
、enumeratorOf<Key>
和memberOf<Key>
:(对应于NSSet类定义的原语方法)的三个方法。如果找到了所有三个方法,则创建一个集合代理对象来响应所有NSSet方法并返回该对象。否则,请执行步骤4。
该代理对象随后将它接收到的任何NSSet消息转换为countOf
<Key>
、enumeratorOf<Key>
和memberOf<Key>
:消息的某种组合到创建它的对象。实际上,与键值编码兼容的对象一起工作的代理对象允许底层属性的行为就像它是一个NSSet,即使它不是。如果没有找到简单访问器方法或集合访问方法组,并且如果接收方的类方法accessinstancevariablesdirect返回YES,则按此顺序搜索一个名为
_<key>
, _is< key>
,<key>
,或 is<key>
的实例变量。如果找到,直接获取实例变量的值,然后执行步骤5。否则,执行步骤6。如果检索到的属性值是一个对象指针,则只需返回结果。
如果该值是NSNumber支持的标量类型,则将其存储在NSNumber实例中并返回该值。
如果结果是NSNumber不支持的标量类型,则转换为NSValue对象并返回。
如果所有这些都失败了,调用valueForUndefinedKey:。默认情况下这会引发异常,但NSObject的子类可以提供特定于键的行为。
Setter的搜索模式(Search Pattern for the Basic Setter)
setValue:forKey:的默认实现,给定键和值参数作为输入,尝试在接收调用的对象内部将名为key的属性设置为value。其实现遵循以下流程:
- 按此顺序查找第一个名为set
<Key>
:或_set<Key>
的访问器。如果找到,就用输入值(或根据需要解包value)调用它并完成。 - 如果没有找到简单的访问器,并且类方法accessinstancevariablesdirect返回YES,则查找名称类似于
_<key>
、_is<key>
、<key>
或is<key>
的实例变量。如果找到,直接用输入值(或解包值)设置变量并完成。 - 在没有找到访问器或实例变量时,调用setValue:forUndefinedKey:。默认情况下这会引发异常,但NSObject的子类可以提供特定于键的行为。