KVO(Key-Value Observing,键值观察)
是什么?
KVO 是 Objective-C/Swift 中的一种 观察者模式,允许对象监听另一个对象的属性变化。当被监听属性的值被修改时,观察者会自动收到通知。
核心机制
-
动态子类:
- 当对象首次被监听时,Runtime 会动态生成一个子类(如
NSKVONotifying_ClassName
)。 - 重写被监听属性的
setter
方法,插入willChangeValueForKey:
和didChangeValueForKey:
方法。
- 当对象首次被监听时,Runtime 会动态生成一个子类(如
-
消息转发:
- 修改对象的
isa
指针,指向动态子类,隐藏实现细节。 - 当属性被修改时,通过
didChangeValueForKey:
触发观察者的回调。
- 修改对象的
用途
- 数据与 UI 同步:例如,模型数据变化时自动更新界面。
- 跨组件通信:解耦不同模块间的依赖关系。
示例
objc
// 监听 person 的 age 属性
[person addObserver:self
forKeyPath:@"age"
options:NSKeyValueObservingOptionNew
context:nil];
// 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:@"age"]) {
NSLog(@"新年龄:%@", change[NSKeyValueChangeNewKey]);
}
}
KVC(Key-Value Coding,键值编码)
是什么?
KVC 是一种通过 字符串键(Key)间接访问对象属性 的机制,无需直接调用 getter/setter
方法,甚至可以直接访问私有成员变量。
核心机制
-
方法优先:
- 赋值 (
setValue:forKey:
)时,优先查找setKey:
或_setKey:
方法。 - 取值 (
valueForKey:
)时,优先查找getKey
、key
、isKey
、_key
方法。
- 赋值 (
-
成员变量次之:
- 若未找到方法,且
+accessInstanceVariablesDirectly
返回YES
,则按顺序访问成员变量:
_key
→_isKey
→key
→isKey
。
- 若未找到方法,且
用途
- 动态配置属性:通过字符串灵活读写属性(如解析 JSON 数据)。
- 访问私有变量:调试或特殊场景下访问私有成员。
- 简化代码 :避免硬编码大量的
getter/setter
调用。
示例
objc
// 通过 KVC 设置属性
[person setValue:@25 forKey:@"age"];
// 通过 KVC 读取属性
NSNumber *age = [person valueForKey:@"age"];
KVO 与 KVC 的区别
特性 | KVO | KVC |
---|---|---|
核心功能 | 监听属性变化 | 通过字符串键间接访问属性 |
底层机制 | 动态生成子类,重写 setter | 方法查找 + 成员变量访问 |
主要用途 | 数据同步、跨组件通信 | 动态读写属性、访问私有变量 |
触发条件 | 必须通过 setter 或 KVC 修改属性 | 直接通过 setValue:forKey: 操作 |
复杂度 | 需手动管理观察者生命周期 | 无需额外管理 |
常见问题
1. 为什么 KVO 监听属性必须用字符串 KeyPath?
- 动态性:字符串 KeyPath 允许运行时动态绑定属性,但容易拼写错误(编译器不检查)。
2. KVC 能修改私有变量吗?
- 可以 :若
+accessInstanceVariablesDirectly
返回YES
,且私有变量命名符合 KVC 规范(如_key
)。
3. KVO 不移除观察者会崩溃吗?
- 会 :观察者释放后,若被观察对象仍发送通知,会导致野指针访问(
EXC_BAD_ACCESS
)。
总结
-
KVO 是 iOS 中实现 属性监听 的核心机制,依赖 Runtime 动态派发。
-
KVC 提供了一种 灵活访问属性 的方式,但需注意命名规范和安全性。
-
适用场景:
- KVO:数据驱动 UI、跨层级通信。
- KVC:动态配置、反射、访问私有变量(谨慎使用)。