键路径(keyPath)、键值编码(KVC)和键值观察(KVO)详解
1. 键路径(KeyPath)
定义:
键路径是用字符串表示的属性访问路径,支持嵌套属性(如 address.city)。
作用:
- 通过字符串动态访问属性,解耦代码与具体属性名的依赖。
- 用于 KVC 和 KVO 中,指定需要操作或观察的属性路径。
原理:
- 运行时解析字符串路径,逐级访问对象的属性。
- Swift 中通过
#keyPath在编译时检查路径有效性,避免拼写错误。
代码示例:
// Objective-C
NSString *keyPath = @"address.city"; // 直接使用字符串
// Swift
class Person: NSObject {
@objc var address: Address
}
class Address: NSObject {
@objc var city: String
}
let keyPath = #keyPath(Person.address.city) // 编译时安全检查
2. 键值编码(KVC - Key-Value Coding)
定义:
通过字符串间接访问或修改对象属性的机制,无需直接调用存取方法。
作用:
- 动态读写属性,支持私有属性(需权限)。
- 简化数据操作,常用于数据绑定、序列化等场景。
原理:
- 基于
NSObject的valueForKey:和setValue:forKey:方法。 - 按
get<Key>、<key>、is<Key>顺序查找方法,若未找到则直接访问实例变量。
代码示例:
// Objective-C
Person *person = [Person new];
[person setValue:@"New York" forKeyPath:@"address.city"];
NSString *city = [person valueForKeyPath:@"address.city"];
// Swift
class Person: NSObject {
@objc var name: String = ""
}
let person = Person()
person.setValue("John", forKey: "name") // 写入
let name = person.value(forKey: "name") as? String // 读取
3. 键值观察(KVO - Key-Value Observing)
定义:
一种观察者模式,监听对象属性的变化并通知观察者。
作用:
- 实现数据与 UI 的自动同步(如 Model-View 绑定)。
- 响应属性变化的通用解决方案。
原理:
- 动态生成被观察类的子类,重写
setter方法。 - 修改对象的
isa指针指向新子类,拦截属性赋值操作。 - 在
setter中调用willChangeValueForKey:和didChangeValueForKey:,触发通知。
代码示例:
// Objective-C
// 添加观察者
[person addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew
context:nil];
// 实现回调
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"Name changed: %@", change[NSKeyValueChangeNewKey]);
}
}
// 移除观察者
[person removeObserver:self forKeyPath:@"name"];
// Swift
class Observer: NSObject {
func observe(person: Person) {
person.observe(.name, options: [.new]) { (_, change) in
print("Name changed: (change.newValue ?? "")")
}
}
}
// 被观察类需标记 @objc dynamic
class Person: NSObject {
@objc dynamic var name: String = ""
}
总结对比
| 特性 | KVC | KVO | KeyPath |
|---|---|---|---|
| 核心作用 | 动态读写属性 | 监听属性变化 | 提供属性路径的字符串表示 |
| 依赖 | NSObject 和 @objc 属性 | NSObject、@objc dynamic属性 | 无特殊要求 |
| Swift 兼容 | 需继承 NSObject 和 @objc | 需 @objc dynamic 修饰属性 | 推荐使用 #keyPath |
| 典型场景 | 数据绑定、序列化 | Model-View 同步、状态监听 | 配合 KVC/KVO 使用 |
注意事项:
- KVO 在 Swift 中需严格遵循
@objc dynamic标记。 - 手动触发 KVO 需调用
willChange和didChange方法。 - 使用
#keyPath可避免硬编码字符串的错误。