Links

20.详解KVC

前言

一、 KVC set/get 过程

NSObject 的分类提供了 NSKeyValueCoding协议 的默认实现。
1
本文中的描述使用<key><key>作为键字符串的占位符,该字符串在键值编码协议方法之一中作为参数出现,然后该方法将其用作辅助方法调用或变量名称查找的一部分。映射的属性名称遵循占位符的大小写。例如,对于 getter <key>is<key>,名为 hidden 的属性映射到 hiddenisHidden

1.1 基本 Getter 的搜索过程

valueForKey: 的默认实现,给定一个key参数作为输入,执行以下过程。

1.1.1

在实例中按顺序搜索方法
  • get<key>
  • <key>
  • is<key>
  • _<key>
如果找到,则调用它并使用结果继续执行步骤 5,否则继续下一步
验证一下
@interface RYModel : NSObject {
@public
NSString *_name;
NSString *name;
NSString *isName;
NSString *_isName;
}
@end
getName
name
isName
_name

1.1.2

在实例中搜索名称
  • countOf<key>
  • objectIn<key>AtIndex:
  • <key>AtIndexes:
对应于 NSArray
如果找到这些中的第一个和其他两个中的至少一个,则创建一个响应所有NSArray方法的集合代理对象并返回该对象。否则,继续执行 步骤 3
代理对象随后将任何 NSArray 接收到的一些组合的消息 countOf<key>objectIn<key>AtIndex:<key>AtIndexes: 消息给键-值编码创建它兼容的对象。
如果原始对象还实现了一个可选的方法,其名称类似于 get<key>:range: ,则代理对象也会在适当的时候使用它。
实际上,与 KVC 兼容的对象一起工作的代理对象允许底层属性表现得好像它是 NSArray ,即使它不是。

1.1.3

如果没有找到简单的访问方法或阵列访问方法组,寻找:
  • countOf<key>
  • enumeratorOf<key>
  • memberOf<key>
对应于 NSSet
如果找到所有三个方法,则创建一个响应所有 NSSet 方法的集合代理对象并返回该对象。否则,继续执行步骤 4
此代理对象随后将任何 NSSet 接收到的一些组合信息 countOf<key>enumeratorOf<key>memberOf<key>: 消息以创建它的对象。
实际上,与 KVC 兼容的对象一起工作的代理对象允许底层属性表现得好像它是 NSSet,即使它不是。

1.1.4

如果前面都没有找到,且类方法 accessInstanceVariablesDirectly 返回 YES(默认) ,按顺序查找名为
  • _<key>
  • _is<key>
  • <key>
  • is<key>
的成员变量,如果找到,直接获取实例变量的值并进行步骤5,否则进行步骤6
验证
_name
_isName_
name
isName

1.1.5

如果检索到的属性值是一个对象指针,只需返回结果即可。
如果该值是 NSNumber 支持的基础数据类型,则将其存储在一个 NSNumber 实例中并返回该实例。
如果结果是 NSNumber 不支持的数据类型,则转换为 NSValue 对象并返回。

1.1.6

如果所有其他方法都失败,请调用 valueForUndefinedKey:。 默认情况下,这会引发异常,但NSObject的子类可能会提供特定于Key的处理。

1.2 基本 Setter 的搜索过程

setValue:forKey: 的流程按顺序查找方法
  • set<key>:
  • _set<key>
如果找到,调用。
验证方法查找
setName
_setName
如果没有找到,如果类方法 accessInstanceVariablesDirectly 返回 YES(默认) ,按顺序寻找一个实例变量与名称类似:
  • _<key>
  • _is<key>
  • <key>
  • is<key>
如果找到,设置。
验证变量查找
_name
_isName_
name
_isName_
如果找不到,调用 setValue:forUndefinedKey: 。 默认情况下,这会引发异常,但 NSObject 的子类可能会提供特定于Key的处理。

二、 关于 accessInstanceVariablesDirectly 的思考

控制是否可以通过 KVC 给成员变量赋值,默认 YES
如果 NO,会怎样呢?
accessInstanceVariablesDirectly
这里给了我一点启发,如果自己封装的一些东西不希望使用者通过 KVC 修改一些私有成员变量的话可以在这里返回 NO

参考