iOS-探索KVO|隐秘的角落

311 阅读3分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

Hi 👋

我的个人项目扫雷Elic 无尽天梯梦见账本
类型游戏财务
AppStoreElicUmemi

前言

KVO 在开发中还是经常使用的,但是有些隐秘的角落却经常被忽视,本文将对这些角落进行一些探究。

⚠️重要说明

Important: In order to understand key-value observing, you must first understand key-value coding.

上面是 KVO 官方文档中的一句重要提醒:重要:为了能理解 KVO ,必须先理解 KVC。推荐阅读:iOS-KVC详解

一、 Options

它影响通知中提供的更改字典的内容以及生成通知的方式。

NSKeyValueObservingOptionOld

更改之前的值

NSKeyValueObservingOptionNew

接收新值

NSKeyValueObservingOptionInitial

您指示被观察对象使用选项 NSKeyValueObservingOptionInitial 发送立即更改通知(在 addObserver:forKeyPath:options:context:returns 之前)。您可以使用这个额外的一次性通知来获取初始值。

NSKeyValueObservingOptionPrior

您可以通过包含选项 NSKeyValueObservingOptionPrior 指示被观察对象在属性更改之前发送通知(除了更改之后的通常通知之外)。

变更字典通过包含键 NSKeyValueChangeNotificationIsPriorKey 和包含 YESNSNumber 值来表示预更改通知。

可以通过这些选项的按位枚举接收多种

二、 Context

[tool.ryuk addObserver:tool forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];

在平时使用时,我们一般就传个 NULL 但是他到底是做什么的有想过么?

addObserver:forKeyPath:options:context: 消息中的 Context 指针可以包含任意数据,这些数据将在相应的更改通知中传递回观察者。您可以指定 NULL 并完全依赖键路径字符串来确定更改通知的来源,但是这种方法可能会导致对象的父类由于不同原因也在观察相同的键路径时出现问题。

一种更安全、更可扩展的方法是使用 Context 来确保您收到的通知是发送给您的观察者而不是父类的。

类中唯一命名的静态变量的地址是一个很好的 Context 。在父类或子类中以类似方式选择的 Context 不太可能重叠。您可以为整个类选择一个 Context ,并依靠通知消息中的关键路径字符串来确定发生了什么变化。

或者,您可以为每个观察到的键路径创建一个不同的 Context ,这完全绕过了字符串比较,从而提高了通知解析的效率。

创建 Context 指针

static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;
static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;

注册观察者

- (void)registerAsObserverForAccount:(Account*)account {
    [account addObserver:self
              forKeyPath:@"balance"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                 context:PersonAccountBalanceContext];
 
    [account addObserver:self
              forKeyPath:@"interestRate"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                  context:PersonAccountInterestRateContext];
}

三、 对可变数组的观察

21-01.png

相对可变数组进行 KVO 很可能会写出这样的代码。但是明显是没用的。这就不得不提文章开始的那句官方提示 重要:为了能理解 KVO ,必须先理解 KVC。

访问集合属性 Accessing Collection Properties有提到,为了使可变集合内部的变化,能够被监听到需要调整一下 API

  • mutableArrayValueForKey: and mutableArrayValueForKeyPath:
    • return NSMutableArray
  • mutableSetValueForKey: and mutableSetValueForKeyPath:
    • return NSMutableSet
  • mutableOrderedSetValueForKey: and mutableOrderedSetValueForKeyPath:
    • return NSMutableOrderedSet

21-02.png

四、 KVO 开关控制

如果你写了一个库,不希望内部的一些属性被监听到。有没有办法阻止掉呢?

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;

系统提供了上面的方法,默认是返回 YES,即:所有属性都可以被监听。如果你不希望部分或者全部被监听可以返回 NO

4.1 全部禁止

21-03.png

没有触发变动的通知

4.2 部分禁止

21-04.png

name 的通知没有触发,books 的通知正常触发了。

4.3 禁止后手动发送通知

21-05.png

虽然 automaticallyNotifiesObserversForKey 禁止了通知,但是手动触发后依旧可以正常监听到

参考