这个observeValueForKeyPath:ofObject:change:context:implementation?有什么问题?

时间:2011-07-04 17:59:49

标签: ios key-value-observing

在我的UIScrollView子类中,我正在观察帧更改:

[self addObserver:self forKeyPath:@"frame" options:0 context:NULL];

我的observeValueForKeyPath:ofObject:change:context:实施如下:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (object == self && [keyPath isEqualToString:@"frame"]) {
        [self adjustSizeAndScale];
    }
    if ([UIScrollView instancesRespondToSelector:@selector(observeValueForKeyPath:ofObject:change:context:)]) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; // Exception
    }
}

但是我对这段代码感到异常:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<WLImageScrollView: 0x733a440; baseClass = UIScrollView; frame = (0 0; 320 416); clipsToBounds = YES; layer = <CALayer: 0x7346500>; contentOffset: {0, 0}>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: frame
Observed object: <WLImageScrollView: 0x733a440; baseClass = UIScrollView; frame = (0 0; 320 416); clipsToBounds = YES; layer = <CALayer: 0x7346500>; contentOffset: {0, 0}>
Change: {
    kind = 1;
}
Context: 0x0'

是否意味着UIScrollView实现了observeValueForKeyPath:ofObject:change:context:但是抛出了上述异常?

如果是这样,我如何正确实施observeValueForKeyPath:ofObject:change:context:,以便我既可以处理我感兴趣的更改,又可以让超类有机会处理其感兴趣的更改?

2 个答案:

答案 0 :(得分:16)

添加观察者时应添加context值。在-observeValueForKeyPath方法中,检查上下文参数。如果它不是您添加观察者时传递的上下文,那么您知道此消息不适用于您的子类,并且您可以安全地将其传递给超类。如果相同的值,那么你知道它是给你的,你不应该把它传递给super。

像这样:

static void *myContextPointer;

- (void)addSomeObserver {
    [self addObserver:self forKeyPath:@"frame" options:0 context:&myContextPointer];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context != &myContextPointer) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
    else {
        // This message is for me, do whatever I want with it.
    }
}                                  

答案 1 :(得分:12)

编辑:BJ荷马的回答可能是一个更好的方法来采取这里;我忘记了上下文参数!

即使调用超级实现是本书,但在observeValueForKeyPath:ofObject:change:context:类上调用UIKit并且实际上没有观察到相关字段的NSInternalInconsistency会引发{{1}异常(不是使用无法识别的选择器获得的NSInvalidArgumentException)。异常中的关键字符串表示“收到但未处理”。

据我所知,没有详细记录的方法可以找出对象是否在给定的密钥路径上观察另一个对象。可能存在部分记录的方式,例如-observationInfo属性,据说可以携带对象观察者的信息,但是你自己就是void *

所以我看到它,你有两个选择:要么不调用super实现,要么使用@try / @catch / @finally块来忽略特定类型的NSInternalInconsistencyException。第二种选择可能更具未来性,但我有预感,一些侦探工作可以通过第一个选项让你获得更令人满意的结果。