KVO实现原理
- Runtime动态修改类结构:
- 当观察一个对象的某个属性时,运行时会动态创建一个该类的子类(isa - swizzling)。例如,假设被观察的类是
MyClass
,运行时会创建一个名为NSKVONotifying_MyClass
的子类。
- 这个子类继承自原类,并且会重写被观察属性的
setter
方法。
- 重写setter方法:
- 在新创建的子类中,重写的
setter
方法除了执行原有的属性赋值操作外,还会通知观察者属性值发生了变化。
- 比如原
setter
方法可能是简单的self.property = value
,重写后会类似:
- (void)setProperty:(id)newValue {
[self willChangeValueForKey:@"property"];
[super setProperty:newValue];
[self didChangeValueForKey:@"property"];
}
willChangeValueForKey:
和didChangeValueForKey:
这两个方法会负责通知观察者属性值即将改变和已经改变。
- 实现观察者模式:
- 观察者模式通过
NSObject
的addObserver:forKeyPath:options:context:
方法来实现。
- 当调用这个方法时,会在被观察对象中注册一个观察者,将观察者对象、观察的属性键路径、观察选项等信息存储起来。
- 当属性值改变时,
willChangeValueForKey:
和didChangeValueForKey:
方法会遍历存储的观察者列表,调用观察者的observeValueForKeyPath:ofObject:change:context:
方法,将属性变化信息传递给观察者。
使用KVO可能遇到的问题及解决方案
- 问题:
- 循环引用:如果观察者持有被观察对象,而被观察对象又通过KVO机制回调观察者,可能会导致循环引用,造成内存泄漏。
- 未移除观察者:如果在观察者对象销毁前没有调用
removeObserver:forKeyPath:
方法移除观察者,可能会导致程序崩溃,特别是当被观察对象先于观察者对象销毁时。
- 解决方案:
- 解决循环引用:可以使用
weak
或unowned
(在ARC环境下,Swift中对应weak
和unowned
)来修饰观察者对被观察对象的引用,避免强引用循环。
- 正确移除观察者:在观察者对象的
dealloc
方法中调用removeObserver:forKeyPath:
方法移除观察者,确保在对象销毁前清理KVO相关的注册信息。同时,在观察多个属性时,要确保所有相关的观察都被移除。