KVC / KVO 与 ivar / property 的底层关系

9 阅读3分钟

KVC / KVO 与 ivar / property 的底层关系

关键词:KVC、KVO、ivar、property、Runtime、isa-swizzling


一、为什么 KVC / KVO 一定要和 ivar / property 一起理解

在 Objective-C 中:

  • ivar 是数据的真实存储

  • property 是访问 ivar 的规则

  • KVC / KVO 本质上都是“访问规则之上的机制”

如果不理解 ivar 和 property,就一定理解不清 KVC / KVO


二、KVC(Key-Value Coding)底层原理

1️⃣ 什么是 KVC

KVC 是一种:

通过字符串 key 间接访问对象属性的机制

[person setValue:@"Hanqiu" forKey:@"name"];
NSString *name = [person valueForKey:@"name"];

2️⃣ KVC 的本质

  • 本质是 一套查找规则

  • 最终结果:

    • 要么调用方法

    • 要么直接访问 ivar

📌 KVC 并不依赖 property 是否存在


3️⃣ KVC 的 setValue:forKey: 查找顺序(重点)

当执行:

[person setValue:value forKey:@"name"];

查找顺序如下:

1. setName:
2. _setName:
3. +accessInstanceVariablesDirectly == YES ?
   3.1 _name
   3.2 _isName
   3.3 name
   3.4 isName
4. 调用 setValue:forUndefinedKey:

⚠️ 关键点

  • 默认 +accessInstanceVariablesDirectly 返回 YES
  • KVC 可以绕过 setter,直接改 ivar

4️⃣ KVC 的 valueForKey: 查找顺序

1. getName
2. name
3. isName
4. _name
5. _isName
6. 调用 valueForUndefinedKey:

5️⃣ KVC 与 ivar / property 的关系总结

场景是否需要 property是否访问 ivar
存在 setter
无 setter
无 ivar❌(崩溃)

KVC 是“方法优先,ivar 兜底”的机制****


三、KVO(Key-Value Observing)底层原理

1️⃣ 什么是 KVO

KVO 是一种:

监听属性变化的观察机制

[person addObserver:self
         forKeyPath:@"name"
            options:NSKeyValueObservingOptionNew
            context:nil];

2️⃣ KVO 的本质(一句话)

KVO 监听的是 setter 的调用,而不是 ivar 的变化


3️⃣ KVO 的底层实现机制(核心)

当第一次添加观察者时,系统会:

  1. 动态生成一个子类(NSKVONotifying_XXX)
  2. 修改对象的 isa 指针(isa-swizzling)
  3. 在子类中重写 setter
Person
  ↑ isa
NSKVONotifying_Person

4️⃣ 重写的 setter 做了什么

伪代码如下:

- (void)setName:(id)value {
    [self willChangeValueForKey:@"name"];
    [super setName:value];
    [self didChangeValueForKey:@"name"];
}

👉 通知发生在 setter 内部


5️⃣ 为什么直接修改 ivar 不触发 KVO

_name = @"A";      // ❌ 不触发 KVO
self.name = @"B"; // ✅ 触发 KVO

原因:

  • ivar 赋值不走 setter
  • KVO 根本无法感知

四、KVO 与 property / ivar 的强关联关系

1️⃣ KVO 是否依赖 property?

情况是否支持 KVO
有 setter
只有 ivar
Category property + Associated Object⚠️(可行但危险)

📌 KVO 实际依赖的是 setter,而不是 property 关键字


2️⃣ 手动触发 KVO

如果你必须直接改 ivar:

[self willChangeValueForKey:@"name"];
_name = @"C";
[self didChangeValueForKey:@"name"];

五、KVC + KVO 联合场景分析(高频面试)

场景:用 KVC 修改属性,是否触发 KVO?

[person setValue:@"D" forKey:@"name"];

结论:

  • 如果最终调用 setter → ✅ 触发 KVO

  • 如果直接命中 ivar → ❌ 不触发

是否触发,取决于 KVC 查找路径


六、Runtime 视角看 KVC / KVO

1️⃣ KVC 使用的 Runtime 能力

  • objc_msgSend
  • class_getInstanceVariable
  • object_setIvar

2️⃣ KVO 使用的 Runtime 能力

  • objc_allocateClassPair
  • object_setClass
  • 动态方法重写

七、常见面试陷阱总结

❌ 误区 1:KVO 监听的是 ivar

❌ 错

✔ 监听的是 setter 的调用


❌ 误区 2:没有 property 就不能 KVO

❌ 错

✔ 只要有 setter 方法即可


❌ 误区 3:KVC 一定会触发 KVO

❌ 错

✔ 是否触发取决于是否调用 setter


八、一张关系总图(文字版)

           ┌──────────────┐
           │   property   │
           │ getter/setter│
           └──────┬───────┘
                  │
        KVO 监听   │ setter
                  ▼
               ivar(真实数据)
                  ▲
                  │
            KVC 兜底访问

九、终极总结

KVC 是“方法优先、ivar 兜底”的键值访问机制;KVO 是通过 isa-swizzling 重写 setter 来监听属性变化的机制,本质与 ivar 无关,只与 setter 是否被调用有关。