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 的底层实现机制(核心)
当第一次添加观察者时,系统会:
- 动态生成一个子类(NSKVONotifying_XXX)
- 修改对象的 isa 指针(isa-swizzling)
- 在子类中重写 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 是否被调用有关。