MST

星途 面试题库

面试题:Objective-C Runtime对KVO实现的深度剖析

从Objective-C运行时角度深入分析KVO(Key - Value Observing)是如何实现的。包括Runtime如何动态修改类的结构、如何重写setter方法以及如何实现观察者模式,同时说明在使用KVO时可能遇到的问题及解决方案。
37.8万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

KVO实现原理

  1. Runtime动态修改类结构
    • 当观察一个对象的某个属性时,运行时会动态创建一个该类的子类(isa - swizzling)。例如,假设被观察的类是MyClass,运行时会创建一个名为NSKVONotifying_MyClass的子类。
    • 这个子类继承自原类,并且会重写被观察属性的setter方法。
  2. 重写setter方法
    • 在新创建的子类中,重写的setter方法除了执行原有的属性赋值操作外,还会通知观察者属性值发生了变化。
    • 比如原setter方法可能是简单的self.property = value,重写后会类似:
- (void)setProperty:(id)newValue {
    [self willChangeValueForKey:@"property"];
    [super setProperty:newValue];
    [self didChangeValueForKey:@"property"];
}
  • willChangeValueForKey:didChangeValueForKey:这两个方法会负责通知观察者属性值即将改变和已经改变。
  1. 实现观察者模式
    • 观察者模式通过NSObjectaddObserver:forKeyPath:options:context:方法来实现。
    • 当调用这个方法时,会在被观察对象中注册一个观察者,将观察者对象、观察的属性键路径、观察选项等信息存储起来。
    • 当属性值改变时,willChangeValueForKey:didChangeValueForKey:方法会遍历存储的观察者列表,调用观察者的observeValueForKeyPath:ofObject:change:context:方法,将属性变化信息传递给观察者。

使用KVO可能遇到的问题及解决方案

  1. 问题
    • 循环引用:如果观察者持有被观察对象,而被观察对象又通过KVO机制回调观察者,可能会导致循环引用,造成内存泄漏。
    • 未移除观察者:如果在观察者对象销毁前没有调用removeObserver:forKeyPath:方法移除观察者,可能会导致程序崩溃,特别是当被观察对象先于观察者对象销毁时。
  2. 解决方案
    • 解决循环引用:可以使用weakunowned(在ARC环境下,Swift中对应weakunowned)来修饰观察者对被观察对象的引用,避免强引用循环。
    • 正确移除观察者:在观察者对象的dealloc方法中调用removeObserver:forKeyPath:方法移除观察者,确保在对象销毁前清理KVO相关的注册信息。同时,在观察多个属性时,要确保所有相关的观察都被移除。