6-1.【OC】【KVC/KVO】KVC 访问属性时的查找顺序是什么?

0 阅读2分钟

KVC(Key-Value Coding)的属性查找逻辑是一个非常严密的“搜索算法”。它不仅查找 Getter/Setter 方法,还会根据特定规则尝试访问成员变量(Ivar)。

查找顺序主要分为 “设值 (Set)”“取值 (Get)” 两个流程。


1. 设值流程:setValue:forKey:

当执行 setValue:value forKey:@"name" 时,查找顺序如下:

  1. 查找 Setter 方法:

    按照顺序查找:setName: \rightarrow _setName:。如果找到,直接传递参数执行。

  2. 查找接入点:

    如果没有 Setter,KVC 会检查类方法 +accessInstanceVariablesDirectly

    • 如果返回 NO(禁止直接访问变量),则跳转到第 4 步。
    • 如果返回 YES(默认值),则进入第 3 步。
  3. 按顺序查找成员变量(Ivar):

    依次查找:_name \rightarrow _isName \rightarrow name \rightarrow isName

  4. 最终处理:

    如果以上都没找到,调用 -setValue:forUndefinedKey:(默认抛出异常 NSUndefinedKeyException)。


2. 取值流程:valueForKey:

当执行 [obj valueForKey:@"name"] 时,查找过程更为复杂,因为它涉及到了对集合类型的特殊支持:

  1. 查找 Getter 方法:

    依次查找:getName \rightarrow name \rightarrow isName \rightarrow _name。如果找到,执行并返回结果。

  2. 查找集合搜索方法(特殊优化):

    如果没找到普通 Getter,KVC 会尝试寻找符合集合模式的方法(如 countOfNameobjectInNameAtIndex: 等)。如果找到,它会返回一个能够响应集合操作的代理对象

  3. 查找接入点:

    检查 +accessInstanceVariablesDirectly 是否返回 YES

  4. 按顺序查找成员变量(Ivar):

    依次查找:_name \rightarrow _isName \rightarrow name \rightarrow isName

  5. 最终处理:

    如果还是没找到,调用 -valueForUndefinedKey:


3. 核心机制总结

阶段搜索优先级 (Key: "name")
第一阶段 (Method)setName: (Set) / getName (Get)
第二阶段 (Access Check)+accessInstanceVariablesDirectly
第三阶段 (Ivar)下划线开头优先 (_name > _isName > name)
第四阶段 (Fallback)UndefinedKey 异常处理

4. 为什么 KVC 能够访问私有变量?

KVC 的强大(也伴随风险)之处在于它跳过了编译器的访问权限检查。即使你的 _name 定义在 .m 文件的类扩展中且没有暴露接口,KVC 依然能通过 Runtime 指针偏移 找到该变量并强行读写。

面试小贴士:

如果面试官问“如何禁用 KVC 访问私有变量?”,答案是重写 +accessInstanceVariablesDirectly 并返回 NO。此时 KVC 只能访问定义了 Setter/Getter 的属性。