MST

星途 面试题库

面试题:Objective-C中KVO的深层次监听与性能优化

在Objective-C里,如果要监听一个对象内部嵌套对象的属性变化(深层次监听),该如何实现?这种深层次监听可能会带来哪些性能问题,又该如何优化?
38.4万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

实现深层次监听的方法

  1. 使用KVO(Key-Value Observing)结合递归
    • 为外层对象添加KVO监听,当外层对象被监听的属性发生变化时,检查该属性是否为对象类型。
    • 如果是对象类型,为该嵌套对象的相关属性再添加KVO监听,以此类推,实现递归监听。
    • 示例代码如下:
// 假设OuterClass有一个InnerClass类型的属性innerObject
@interface OuterClass : NSObject
@property (nonatomic, strong) InnerClass *innerObject;
@end

@interface InnerClass : NSObject
@property (nonatomic, strong) NSString *name;
@end

@implementation OuterClass
- (void)addDeepObserving {
    [self addObserver:self forKeyPath:@"innerObject" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"innerObject"]) {
        InnerClass *newInnerObject = change[NSKeyValueChangeNewKey];
        if (newInnerObject) {
            [newInnerObject addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
        }
    } else if ([keyPath isEqualToString:@"innerObject.name"]) {
        // 处理innerObject的name属性变化
        NSString *newName = change[NSKeyValueChangeNewKey];
        NSLog(@"Inner object name changed to: %@", newName);
    }
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"innerObject"];
    if (self.innerObject) {
        [self.innerObject removeObserver:self forKeyPath:@"name"];
    }
}
@end
  1. 使用通知(NSNotification)
    • 在嵌套对象内部,当属性变化时,发送通知。
    • 外层对象注册接收这些通知来实现监听。
    • 例如,在InnerClass的属性setter方法中发送通知:
@implementation InnerClass
- (void)setName:(NSString *)name {
    _name = name;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"InnerObjectNameChanged" object:self userInfo:@{@"newName": name}];
}
@end

@implementation OuterClass
- (void)addDeepObserving {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInnerObjectNameChange:) name:@"InnerObjectNameChanged" object:nil];
}

- (void)handleInnerObjectNameChange:(NSNotification *)notification {
    NSString *newName = notification.userInfo[@"newName"];
    NSLog(@"Inner object name changed to: %@", newName);
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"InnerObjectNameChanged" object:nil];
}
@end

性能问题

  1. 大量KVO监听开销:如果嵌套层次较深且对象数量较多,为每个对象的属性添加KVO监听会消耗大量系统资源,包括内存和CPU时间。每次属性变化时,KVO机制都要进行通知的分发和处理,这会增加系统的负担。
  2. 递归处理的性能消耗:在递归添加KVO监听过程中,需要不断检查对象类型并添加新的监听,这会带来额外的性能开销,尤其是在对象结构复杂时。
  3. 通知的频繁发送与处理:使用通知机制时,如果属性变化频繁,频繁发送和接收通知会导致性能下降。通知中心需要处理大量的通知分发,接收者也需要频繁响应通知。

优化方法

  1. 减少不必要的监听:仔细评估哪些嵌套对象的属性真正需要监听,避免对无关属性添加监听。例如,只对可能影响业务逻辑的关键属性进行监听。
  2. 批量处理变化:可以设置一个标志位或使用队列,将多次属性变化合并处理,而不是每次变化都立即响应。这样可以减少处理频率,提高性能。
  3. 使用更轻量级的机制:对于一些简单的场景,可以考虑使用代理模式代替KVO或通知。代理模式可以更精准地控制事件的传递,避免全局通知带来的性能开销。同时,代理模式相对KVO来说,实现更简单,资源消耗也更小。