可能遇到的问题
- 内存泄漏:如果观察者对象持有被观察对象的强引用,而被观察对象又对观察者进行注册KVO,可能形成循环引用,导致内存泄漏。
- 数据竞争:多线程环境下,不同线程可能同时对被观察属性进行修改和监听,可能导致监听逻辑执行顺序混乱,数据不一致等问题。
解决方案及思路
- 解决内存泄漏:
- 使用弱引用:在观察者对象中使用
__weak
修饰符来持有被观察对象的引用,避免循环引用。例如:
__weak id observedObjectWeakRef = observedObject;
[observedObjectWeakRef addObserver:self forKeyPath:@"someProperty" options:NSKeyValueObservingOptionNew context:nil];
- **在合适时机移除观察者**:在观察者对象的`dealloc`方法中,移除对被观察对象的KVO监听。
- (void)dealloc {
[self.observedObject removeObserver:self forKeyPath:@"someProperty"];
}
- 解决数据竞争:
- 使用队列:将KVO的注册、移除以及监听回调逻辑都放在同一个队列(如
dispatch_queue_t
)中执行。这样可以保证这些操作是串行执行,避免多线程竞争。
dispatch_queue_t queue = dispatch_queue_create("com.example.kvoQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[self.observedObject addObserver:self forKeyPath:@"someProperty" options:NSKeyValueObservingOptionNew context:nil];
});
// 在监听回调中也使用该队列
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
dispatch_async(queue, ^{
// 处理属性变化逻辑
});
}
- **使用锁**:在对被观察属性进行修改和KVO相关操作时,使用锁(如`@synchronized`块或`NSLock`)来保证同一时间只有一个线程能进行操作。
@synchronized(self.observedObject) {
self.observedObject.someProperty = newValue;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
@synchronized(object) {
// 处理属性变化逻辑
}
}