面试题答案
一键面试KVO实现原理
- 动态生成子类:
- 当一个对象(假设为
subject
)被观察时,运行时系统会动态创建该对象类的一个子类。例如,如果被观察对象是NSObject
的某个子类MyClass
,系统会创建一个MyClass
的子类,命名可能类似NSKVONotifying_MyClass
。 - 这个动态生成的子类继承自原始类,拥有原始类的所有属性和方法。
- 当一个对象(假设为
- 重写setter方法:
- 对于被观察的属性,动态生成的子类会重写其对应的setter方法。例如,对于属性
name
,原始类可能有- (void)setName:(NSString *)name
方法。在动态子类中,这个方法会被重写。 - 在重写的setter方法中,首先会调用父类(即原始类)的setter方法来实际设置属性的值。然后,它会通知观察者属性值发生了变化。通知过程是通过
NSNotificationCenter
来实现的,系统会发布一个NSKeyValueChangeNotification
通知,包含了属性变化的相关信息,如变化的类型(新值、旧值等)。
- 对于被观察的属性,动态生成的子类会重写其对应的setter方法。例如,对于属性
- isa混写:
- 系统会将被观察对象的
isa
指针指向动态生成的子类。这样,当调用被观察对象的setter方法时,实际上调用的是动态子类重写后的setter方法,从而实现属性变化的通知。
- 系统会将被观察对象的
KVO使用的局限性
- 仅适用于属性变化:
- KVO只能观察属性值的变化,对于对象内部其他状态的改变(如方法调用导致的内部逻辑变化,但属性值未改变)无法进行观察。例如,一个对象的某个方法执行了复杂的计算,但没有改变任何可观察属性,KVO无法感知这种变化。
- 依赖于KVC:
- KVO是基于KVC(Key - Value Coding)实现的,这意味着它要求被观察的属性遵循KVC的命名和访问规则。如果属性的访问方法不符合KVC规范,KVO可能无法正常工作。
- 性能开销:
- 动态生成子类以及重写setter方法等操作会带来一定的性能开销。特别是在有大量对象被观察的情况下,内存和CPU的消耗会比较明显。
- 难以调试:
- 由于KVO是在运行时动态生成子类和重写方法,调试过程相对复杂。当出现问题时,很难直接定位到是KVO机制本身的问题还是其他代码的问题,因为动态生成的子类和相关逻辑在代码中不是显式可见的。
- 循环引用问题:
- 如果观察者和被观察对象之间形成循环引用,可能会导致内存泄漏。例如,一个对象A观察对象B,而对象B又持有对象A的引用,这种情况下如果不妥善处理,会造成内存无法释放。