【问题标题】:What is the context parameter used for in Key value observing键值观察中使用的上下文参数是什么
【发布时间】:2012-08-08 15:09:28
【问题描述】:

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

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

有人可以解释一下它背后的目的是什么......

【问题讨论】:

标签: objective-c cocoa cocoa-touch key-value-observing


【解决方案1】:

我希望这个解释不要太抽象而难以理解。

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

现在你决定让MyViewController 使用KVO 来观察self.viewcenter 属性的变化。所以你适当地将自己添加为观察者:

- (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.viewcenter 的观察者。如果是这样,那么您可能会遇到两个问题:

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

您需要一种将自己注册为观察者的方法,该方法与UIViewController 的KVO 注册不同。这就是context 参数的用武之地。您需要为context 传递一个值,您绝对确定UIViewController 不是 用作context 参数。当您取消注册时,您会再次使用相同的context,这样您只会删除您的注册,而不是UIViewController 的注册。在您的 observeValueForKeyPath:ofObject:change:context: 方法中,您需要检查 context 以查看该消息是给您的,还是给您的超类的。

确保您使用没有其他用途的context 的一种方法是在MyViewController.m 中创建一个static 变量。在注册和注销时使用它,如下所示:

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];
    }
}

【讨论】:

  • 非常感谢您的详细解释。非常感谢。一段时间以来,我一直困惑为什么这个参数很重要,而你的回答澄清了这一点。 :)
  • 您好,我可以使用上下文来设置一些对象,然后当通知到来时检索它或根据上下文中存储的值执行一些特定任务。
  • +1 精彩的解释。 (一个警告:UIView 的属性不符合 KVO。您无法观察到 view.center。)
  • @robmayoff 所以这是唯一的情况?
  • @onmyway133 基于上下文而不是关键路径调度也稍微快一些。我已经更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 2023-03-06
  • 2011-04-04
  • 2011-03-29
  • 1970-01-01
  • 2012-08-25
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
相关资源
最近更新 更多