MST

星途 面试题库

面试题:Objective-C中KVO与多线程及内存管理

在多线程环境下使用Objective-C的KVO进行属性变化监听,可能会遇到哪些问题?如何在这种场景下确保内存管理的正确性,避免内存泄漏和数据竞争等问题?请详细阐述你的解决方案及思路。
20.7万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

可能遇到的问题

  1. 内存泄漏:如果观察者对象持有被观察对象的强引用,而被观察对象又对观察者进行注册KVO,可能形成循环引用,导致内存泄漏。
  2. 数据竞争:多线程环境下,不同线程可能同时对被观察属性进行修改和监听,可能导致监听逻辑执行顺序混乱,数据不一致等问题。

解决方案及思路

  1. 解决内存泄漏
    • 使用弱引用:在观察者对象中使用__weak修饰符来持有被观察对象的引用,避免循环引用。例如:
__weak id observedObjectWeakRef = observedObject;
[observedObjectWeakRef addObserver:self forKeyPath:@"someProperty" options:NSKeyValueObservingOptionNew context:nil];
- **在合适时机移除观察者**:在观察者对象的`dealloc`方法中,移除对被观察对象的KVO监听。
- (void)dealloc {
    [self.observedObject removeObserver:self forKeyPath:@"someProperty"];
}
  1. 解决数据竞争
    • 使用队列:将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) {
        // 处理属性变化逻辑
    }
}