KVO的实现是基于Runtime机制的,主要通过动态创建子类并重写setter方法来实现。具体步骤如下:
- 动态创建子类
- 当你对一个对象第一次调用
addObserver:forKeyPath:options:context:
方法时 - 系统会动态创建一个新的类,类名通常为
NSKVONotifying_原类名
- 这个新类继承自原类
-
重写setter方法
新创建的子类中重写了被观察属性的setter方法,大致实现如下:
- (void)setSomeProperty:(id)newValue {
[self willChangeValueForKey:@"someProperty"];
// 调用父类的setter方法
[super setSomeProperty:newValue];
[self didChangeValueForKey:@"someProperty"];
}
- 修改isa指针
- 系统将对象的isa指针指向新创建的子类
- 这样当属性发生改变时,就会调用子类重写的setter方法
具体实现示例
// 原始类
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
// 使用KVO
Person *person = [[Person alloc] init];
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
当添加观察者后,Runtime会创建一个新的类:
// KVO动态创建的子类(简化版)
@interface NSKVONotifying_Person : Person
@end
@implementation NSKVONotifying_Person
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
[super setName:name];
[self didChangeValueForKey:@"name"];
}
// 重写class方法,隐藏KVO的实现细节
- (Class)class {
return [Person class];
}
@end
验证KVO的实现
你可以通过以下代码验证KVO的实现原理:
Person *person = [[Person alloc] init];
NSLog(@"Before KVO: %@", object_getClass(person));
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"After KVO: %@", object_getClass(person));
输出结果会类似:
Before KVO: Person
After KVO: NSKVONotifying_Person
注意事项
- KVO是自动触发的,但必须通过setter方法修改属性才能触发
- 直接修改实例变量不会触发KVO
- 必须在不再需要观察时移除观察者,否则可能导致崩溃
- iOS 12之后,苹果引入了新的KVO API,使用block的方式更加安全和便捷
// 新的KVO API
person.observe(\.name, options: [.new]) { object, change in
print("name changed to: \(change.newValue)")
}
这就是KVO的基本实现原理,它是iOS中一个非常重要的观察者模式的实现。