返回

揭秘OC底层原理之KVO:20节深入剖析

IOS

KVO(键值观察),是Objective-C中一个强大的机制,用于监视对象属性的变化。它允许开发者在属性被修改时收到通知,从而在需要时更新UI或执行其他任务。

在本文中,我们将深入探讨KVO的底层原理,包括它的内部机制、实现细节以及一些高级用法。我们将分20个小节来分析KVO,以便于理解和消化。

一、KVO细节分析(上)

1.关于context的细节

官方文档中关于context的

context 是一个在KVO中观察到的属性所属的 context。它可能是一个任意类型的对象。其目的是提供一个KVO观察到的属性所属的上下文。这对于组织和管理观察到的属性很有用,特别是当有多个观察者观察同一个对象时。

通俗来讲,context的作用就是为KVO观察到的属性提供一个上下文环境,它可以是一个任意类型的对象,比如一个特定类或一个特定实例。有了context,开发者可以根据不同的上下文来组织和管理观察到的属性,尤其是在多个观察者同时观察同一个对象时。

例如,我们可以为每个视图控制器创建一个唯一的context,然后使用这个context来观察视图控制器中的属性。这样,当视图控制器中的属性发生改变时,只有与该视图控制器相关的观察者才会收到通知,而其他观察者不会受到影响。

二、KVO的观察者(上)

2.NSKeyValueObservingOptions详解

NSKeyValueObservingOptions是一个枚举类型,它提供了各种选项来控制KVO的观察行为。这些选项可以用来指定观察的属性是否是新的、旧的、优先级、是否观察依赖项等。

选项
NSKeyValueObservingOptionNew 指定观察新的属性值。
NSKeyValueObservingOptionOld 指定观察旧的属性值。
NSKeyValueObservingOptionPrior 指定在更改应用之前观察属性值。
NSKeyValueObservingOptionInitial 指定在注册观察者时立即观察属性值。
NSKeyValueObservingOptionDependent 指定观察属性的依赖项,当依赖项发生变化时触发观察。

开发者可以通过组合这些选项来创建自定义的观察行为。例如,以下代码将观察一个对象的“name”属性,并在每次更改时同时获取新旧属性值:

[object addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];

三、KVO的实现(上)

3.Runtime Injection

KVO的实现使用了Runtime Injection技术,它允许在运行时动态修改对象的类和方法。当开发者注册一个KVO观察者时,Runtime Injection会将方法添加到对象的类中,这些方法会在属性发生改变时触发。

例如,当我们为“name”属性添加一个观察者时,Runtime Injection会在对象的类中添加一个名为“setNameAndNotify”的方法。当“name”属性被修改时,这个方法就会被调用,从而通知观察者属性的变化。

这种技术的好处是它允许KVO在不修改原始类的情况下观察属性。这使得KVO具有高度的可扩展性和灵活性。

四、KVO的注意点(上)

4.属性的类型和访问控制

KVO只能观察实例变量或属性,而不能观察方法或其他类型的成员。此外,KVO只能观察公开的属性,不能观察私有属性。

如果开发者尝试观察一个私有属性,KVO将抛出一个异常。为了观察私有属性,开发者可以使用Runtime Injection手动创建一个观察者,但这种方法不推荐使用。

5.循环引用和内存管理

KVO观察者和被观察对象之间可能存在循环引用,这可能会导致内存泄漏。为了防止这种情况,KVO使用弱引用来持有观察者。

当被观察对象释放时,KVO会自动移除所有观察者,从而打破循环引用。但是,如果观察者持有对被观察对象的强引用,则可能会发生内存泄漏。

为了避免内存泄漏,开发者应该始终使用弱引用来持有被观察对象。例如,以下代码使用弱引用来防止内存泄漏:

__weak typeof(self) weakSelf = self;
[object addObserver:weakSelf forKeyPath:@"name" options:...]