MST
星途 面试题库

面试题:Objective-C中KVO在复杂场景下的应用与优化

假设在一个大型的Objective-C项目中,存在多个视图控制器之间通过KVO进行数据监听和交互,出现了性能问题。请分析可能的原因,并提出优化方案,同时说明如何确保KVO在多线程环境下的稳定性。
46.4万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

可能原因分析

  1. 过多不必要的观察:在多个视图控制器间,如果大量设置KVO观察,尤其是对频繁变化的属性观察,会造成大量的通知发送和处理,消耗性能。
  2. 观察逻辑复杂:观察回调方法中执行复杂计算、大量I/O操作或频繁更新UI等,会导致主线程阻塞,影响性能。
  3. 未正确移除观察:视图控制器销毁时若未正确移除KVO观察,会导致观察者泄漏,不仅占用内存,还可能继续接收通知,引发异常和性能问题。

优化方案

  1. 精简观察:仔细梳理业务,仅对真正需要监听且变化频率合理的属性设置KVO观察。
  2. 简化观察逻辑:在观察回调方法中尽量只做数据更新相关操作,复杂计算放到后台线程执行,更新UI操作则切回主线程。例如:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"yourKeyPath"]) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 复杂计算
            id newValue = change[NSKeyValueChangeNewKey];
            // 计算结果
            dispatch_async(dispatch_get_main_queue(), ^{
                // 更新UI
            });
        });
    }
}
  1. 正确移除观察:在视图控制器的dealloc方法中移除KVO观察,例如:
- (void)dealloc {
    [self.observedObject removeObserver:self forKeyPath:@"yourKeyPath"];
}

多线程环境下稳定性确保

  1. 指定线程接收通知:在添加KVO观察时,通过NSKeyValueObservingOptionInitialNSKeyValueObservingOptionNew选项,指定在哪个线程接收通知。例如在主线程接收通知:
[self.observedObject addObserver:self forKeyPath:@"yourKeyPath" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:nil];
  1. 线程安全的属性访问:被观察对象的属性在多线程环境下访问时,要保证线程安全。可以使用锁(如NSLock@synchronized)或dispatch_queue来保护属性访问。例如:
@property (nonatomic, strong) NSLock *propertyLock;
- (void)setYourProperty:(id)newValue {
    [self.propertyLock lock];
    _yourProperty = newValue;
    [self.propertyLock unlock];
}
- (id)yourProperty {
    [self.propertyLock lock];
    id value = _yourProperty;
    [self.propertyLock unlock];
    return value;
}
  1. 避免跨线程操作问题:确保KVO回调中的操作与所在线程兼容,避免在子线程更新UI等不允许的跨线程操作。在需要跨线程传递数据时,使用线程安全的方式,如dispatch_async等。