20.详解KVC

前言

一、 KVC set/get 过程

NSObject 的分类提供了 NSKeyValueCoding协议 的默认实现。

本文中的描述使用<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

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

验证

1.1.5

如果检索到的属性值是一个对象指针,只需返回结果即可。

如果该值是 NSNumber 支持的基础数据类型,则将其存储在一个 NSNumber 实例中并返回该实例。

如果结果是 NSNumber 不支持的数据类型,则转换为 NSValue 对象并返回。

1.1.6

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

1.2 基本 Setter 的搜索过程

setValue:forKey: 的流程按顺序查找方法

  • set<key>:

  • _set<key>

如果找到,调用。

验证方法查找

如果没有找到,如果类方法 accessInstanceVariablesDirectly 返回 YES(默认) ,按顺序寻找一个实例变量与名称类似:

  • _<key>

  • _is<key>

  • <key>

  • is<key>

如果找到,设置。

验证变量查找

如果找不到,调用 setValue:forUndefinedKey: 。 默认情况下,这会引发异常,但 NSObject 的子类可能会提供特定于Key的处理。

二、 关于 accessInstanceVariablesDirectly 的思考

控制是否可以通过 KVC 给成员变量赋值,默认 YES

如果 NO,会怎样呢?

这里给了我一点启发,如果自己封装的一些东西不希望使用者通过 KVC 修改一些私有成员变量的话可以在这里返回 NO

参考

Key-Value Coding Programming Guide

Last updated