Что не так с этим наблюдениемValueForKeyPath: fromObject: change: context: implementation?
В моем подклассе 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:
, чтобы я мог обрабатывать мои заинтересованные изменения и дать суперклассу возможность обработать его интересующие изменения?
Ответы
Ответ 1
Изменить: ответ BJ Гомера, вероятно, лучший подход к этому; Я забыл все о параметре контекста!
Несмотря на то, что вызов супер-реализации - это книга, она кажется, вызывающей observeValueForKeyPath:ofObject:change:context:
на UIKit
классах, Фактически наблюдаемые поля задают исключение NSInternalInconsistency
(а не NSInvalidArgumentException
, которое вы получите с непризнанным селектором). Строка ключа в исключении, которая предлагает это мне, "получена, но не обрабатывается".
Насколько я знаю, нет хорошо документированного способа узнать, наблюдает ли объект другой объект по заданному пути ключа. Могут быть частично документированные способы, такие как свойство -observationInfo
, которое, как говорят, несет информацию о наблюдателях объекта, но вы сами по себе - это a void *
.
Итак, как я вижу, у вас есть два варианта: либо не вызывать реализацию super
, либо использовать блок @try
/@catch
/@finally
, чтобы игнорировать этот тип NSInternalInconsistencyException
, Второй вариант, вероятно, более перспективен, но я подозреваю, что некоторые детективные работы могут получить более удовлетворительные результаты с помощью первого варианта.
Ответ 2
При добавлении наблюдателя вы должны добавить значение context
. В вашем методе -observeValueForKeyPath
проверьте параметр контекста. Если это не тот контекст, который вы передали при добавлении наблюдателя, то вы знаете, что это сообщение не предназначено для вашего подкласса, и вы можете безопасно передать его суперклассу. Если это одно и то же значение, то вы знаете, что оно предназначено для вас, и вы не должны передавать его супер.
Вот так:
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.
}
}