Key值观察中使用的上下文参数是什么

rus*_*ord 27 cocoa cocoa-touch objective-c key-value-observing

在以下方法中使用context参数有什么用于注册键值通知.文档只是将其表示为任意数据集.

addObserver:self forKeyPath:@"selectedIndex" options:NSKeyValueObservingOptionNew context:nil
Run Code Online (Sandbox Code Playgroud)

有人可以说清楚它背后的目的是什么......

rob*_*off 79

我希望这个解释不是太抽象,不能理解.

假设您创建了一个类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];
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是,你不知道是否UIViewController也将自己注册为观察员self.viewcenter.如果是,那么您可能会遇到两个问题:

  1. 当视图的中心发生变化时,您可能会被调用两次.
  2. 当您以观察者身份移除自己时,您也可能会删除UIViewControllerKVO注册.

您需要一种方法来将自己注册为UIViewController与KVO注册区别开来的观察者.这就是在context争论进来.你需要传递一个值context,你有绝对的把握UIViewController不会使用的context参数.当您取消注册时,context再次使用相同的内容,这样您只能删除注册,而不是UIViewController注册.在您的observeValueForKeyPath:ofObject:change:context:方法中,您需要检查context消息是为您还是为您的超类.

确保使用context其他任何其他用途的一种方法是创建一个static变量MyViewController.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];
}
Run Code Online (Sandbox Code Playgroud)

然后在您的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];
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你保证不会干扰你的超类的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];
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1精彩的解释.(一个警告:'UIView`的属性不符合KVO.你无法观察`view.center`.) (11认同)
  • @ onmyway133基于上下文而不是基于关键路径进行调度也要快一些。我已经更新了答案。 (2认同)