我希望这个解释不要太抽象而难以理解。
假设您创建了一个类MyViewController,它是UIViewController 的子类。你没有UIViewController的源代码。
现在你决定让MyViewController 使用KVO 来观察self.view 的center 属性的变化。所以你适当地将自己添加为观察者:
- (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 的center 的观察者。如果是这样,那么您可能会遇到两个问题:
- 当视图的中心发生变化时,您可能会被调用两次。
- 当您将自己作为观察者移除时,您可能还会移除
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];
}
}