КВО - Как проверить, является ли объект наблюдателем? - PullRequest
39 голосов
/ 10 февраля 2012

Наблюдая за значением объекта, используя addObserver:forKeyPath:options:context:, в конечном итоге вы захотите вызвать removeObserver:forKeyPath: для этого объекта, чтобы очистить его позже.Перед тем, как сделать это, возможно ли проверить, действительно ли объект наблюдает за этим свойством?

В своем коде я пытался убедиться, что объект удаляется только тогда, когда это необходимо, ноВ некоторых случаях наблюдатель может попытаться удалить себя дважды.Я работаю, чтобы предотвратить это, но на всякий случай я просто пытался выяснить, есть ли способ сначала проверить, является ли мой код наблюдателем чего-либо.

Ответы [ 3 ]

44 голосов
/ 10 февраля 2012

[...] можно ли проверить, действительно ли объект наблюдает это свойство?

Нет.При работе с КВО вы всегда должны иметь в виду следующую модель:

При создании наблюдения вы несете ответственность за удаление этого точного наблюдения.Наблюдение идентифицируется его контекстом, поэтому контекст должен быть уникальным.При получении уведомлений (а в Lion при удалении наблюдателя) вы всегда должны проверять контекст, а не путь.

Лучший способ обработки наблюдаемых объектов - удаление и установка наблюдения в установщике.наблюдаемого объекта:

static int fooObservanceContext;

- (void)setFoo:(Foo *)foo
{
    [_foo removeObserver:self forKeyPath:@"bar" context:&fooObservanceContext];

    _foo = foo; // or whatever ownership handling is needed.

    [foo addObserver:self forKeyPath:@"bar" options:0 context:&fooObservanceContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == &fooObservanceContext) {
        // handle change
    } else {
        // not my observer callback
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

- (void)dealloc
{
    self.foo = nil; // removes observer
}

При использовании KVO вы должны убедиться, что оба объекта, наблюдатель и наблюдающий, живы, пока наблюдение находится на месте.

При добавленииНаблюдение вы должны сбалансировать это с одним удалением того же самого наблюдения.Не думайте, что вы единственный, кто использует KVO.Каркасные классы могут использовать KVO для своих собственных целей, поэтому всегда проверяйте контекст в обратном вызове.

Еще одна проблема, на которую я хотел бы обратить внимание: наблюдаемое свойство должно быть совместимым с KVO. Вы не можете просто наблюдать за чем-либо .

26 голосов
/ 10 февраля 2012

Часть протокола NSKeyValueObserving такова:

 - (void *)observationInfo

, в котором должны быть перечислены наблюдатели.

EDIT Полезно только для отладки.

1 голос
/ 28 сентября 2018

Я недооцениваю этот объективный вопрос.Но так как многие люди используют Swift / target-c вместе, я подумал, что должен указать на преимущество нового API Swift4 по сравнению с более старыми версиями KVO:

Если вы делаете addObserver несколько раз для KVO,за каждое изменение вы получите observeValue столько же, сколько и текущее количество раз, которое вы добавили в качестве наблюдателя.

  • И чтобы удалить себя, вы должны позвонитьremoveObserver столько раз, сколько вы добавили.
  • Удаление его больше, чем вы добавили, приведет к краху

Swift4 observe намного умнее и быстрее!

  • Если вы делаете это несколько раз, это не волнует.Он не будет выдавать несколько обратных вызовов для каждого изменения.
  • И достаточно только одного invalidate из token.
  • invalidat, перед тем как начать наблюдать, или несколько разто, что вы сделали observe, не приведет к сбою

Поэтому, чтобы конкретно ответить на ваш вопрос, если вы используете новое Swift4 KVO, вам не нужно об этом заботиться.Просто позвоните invalidate и все хорошо.Но если вы используете более старый API, обратитесь к ответу Николая

...