27

在以下用于注册键值通知的方法中,上下文参数的用途是什么。文档只是将其表示为任意数据集。

addObserver:self forKeyPath:@"selectedIndex" options:NSKeyValueObservingOptionNew context:nil

有人可以阐明它背后的目的是什么...

4

1 回答 1

80

我希望这个解释不是太抽象而无法理解。

假设您创建了一个类MyViewController,它是 的子类UIViewController。你没有源代码UIViewController

现在您决定MyViewController使用 KVO 来center观察self.view. 所以你适当地将自己添加为观察者:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.view addObserver:self forKeyPath:@"center" options:0 context:NULL];
}

- (void)viewDidDisappear:(BOOL)animated {
    [self.view removeObserver:self forKeyPath:@"center"];
    [super viewDidDisappear:animated];
}

这里的问题是您不知道是否UIViewController也将自己注册为self.view's的观察者center。如果是这样,那么您可能会遇到两个问题:

  1. 当视图的中心发生变化时,您可能会被调用两次。
  2. 当您将自己作为观察者移除时,您可能还会移除UIViewController的 KVO 注册。

您需要一种将自己注册为观察者的方法,该方法与UIViewController的 KVO 注册不同。这就是context参数的来源。您需要传递一个您绝对context确定不用作参数的值。当您取消注册时,您会再次使用相同的内容,这样您只会删除您的注册,而不是' 注册。在您的方法中,您需要检查消息是否适合您或您的超类。UIViewControllercontextcontextUIViewControllerobserveValueForKeyPath:ofObject:change:context:context

确保您使用context没有其他用途的一种方法是staticMyViewController.m. 注册和注销时使用它,如下所示:

static int kCenterContext;

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.view addObserver:self forKeyPath:@"center" options:0 context:&kCenterContext];
}

- (void)viewDidDisappear:(BOOL)animated {
    [self.view removeObserver:self forKeyPath:@"center" context:&kCenterContext];
    [super viewDidDisappear:animated];
}

然后在你的observeValueForKeyPath:...方法中,像这样检查它:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
    change:(NSDictionary *)change context:(void *)context
{
    if (context == &kCenterContext) {
        // This message is for me.  Handle it.
        [self viewCenterDidChange];
        // Do not pass it on to super!
    } else {
        // This message is not for me; pass it on to super.
        [super observeValueForKeyPath:keyPath ofObject:object
            change:change context:context];
    }
}

现在,您可以保证不会干扰超类的 KVO(如果有的话)。如果有人创建了一个MyViewController也使用 KVO 的子类,它不会干扰您的 KVO。

还要注意,您可以为观察到的每个关键路径使用不同的上下文。然后,当系统通知您更改时,您可以检查上下文而不是检查密钥路径。测试指针相等性比检查字符串相等性快一点。例子:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
    change:(NSDictionary *)change context:(void *)context
{
    if (context == &kCenterContext) {
        [self viewCenterDidChange];
        // Do not pass it on to super!
    } else if (context == &kBackgroundColorContext) {
        [self viewBackgroundDidChange];
        // Do not pass it on to super!
    } else if (context == &kAlphaContext) {
        [self viewAlphaDidChange];
        // Do not pass it on to super!
    } else {
        // This message is not for me; pass it on to super.
        [super observeValueForKeyPath:keyPath ofObject:object
            change:change context:context];
    }
}
于 2012-08-11T20:36:35.953 回答