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

an0*_*an0 15 key-value-observing ios

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

[self addObserver:self forKeyPath:@"frame" options:0 context:NULL];
Run Code Online (Sandbox Code Playgroud)

我的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
    }
}
Run Code Online (Sandbox Code Playgroud)

但是这个代码我得到了异常:

*** 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'
Run Code Online (Sandbox Code Playgroud)

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

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

BJ *_*mer 17

您应该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.
    }
}                                  
Run Code Online (Sandbox Code Playgroud)

  • 老实说,我通常做`static void*myContext =&myContext;`,这很好,因为那时`myContext ==&myContext`,你可以使用其中任何一个.但我并不想在一个与该主题无关的答案中解释那个小混乱. (2认同)

Joe*_*orn 13

编辑:BJ荷马的答案可能是一个更好的方法来采取这里; 我忘了关于context参数的所有内容!

即使调用超级实现由这本书,就好像调用observeValueForKeyPath:ofObject:change:context:UIKit没有实际观察到的领域类的问题抛出一个NSInternalInconsistency异常(而不是NSInvalidArgumentException你会得到具有无法识别的选择).异常中的关键字符串表示"收到但未处理".

据我所知,没有详细记录的方法来确定一个对象是否在给定的密钥路径上观察另一个对象.可能存在部分记录的方式,例如-observationInfo属性被称为携带对象的观察者的信息,但是你自己就在那里 - 它是一个void *.

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

  • 实际上不难知道该消息是否适合您...只需查看使用'context'参数即可. (3认同)