Когда незаконно удалять себя в качестве наблюдателя за моими ключами?

#ios #cocoa #key-value-observing

#iOS #какао #наблюдение за ключом-значением

Вопрос:

Я столкнулся с проблемой в своем приложении. По соображениям удобства моему делегату приложения проще наблюдать за его собственными свойствами. Однако при удалении делегата приложения в качестве наблюдателя я получаю сообщение об ошибке.

Я свел это к небольшому образцу кода и результату (показан позже).

Мой вопрос в том, когда незаконно удалять себя в качестве наблюдателя за моими собственными ключами и как типичный разработчик cocoa решил бы проблему в следующем примере:

из этого кода…

 #import "AppDelegate.h"

@interface Thing : NSObject
@property (nonatomic, strong) Thing * next;
@property (nonatomic, strong) id value;
@end

@implementation Thing
@synthesize next,value;
 (Thing*)thing
{
    return [[Thing new] autorelease];
}
@end

@interface AppDelegate ()
@property (strong, nonatomic) Thing * thing;
@end

@implementation AppDelegate

@synthesize window = _window, thing;

- (void)dealloc
{
    [super dealloc];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application

    Thing * thing2 = [Thing thing];
    thing2.value = @"hello";

    Thing * thing1 = [Thing thing];
    thing1.next = thing2;
    self.thing = thing1;

    [self addObserver:self forKeyPath:@"thing.next.value" options:0 context:NULL];
    [self addObserver:self forKeyPath:@"thing.next" options:0 context:NULL];

    Thing * thing3 = [Thing thing];
    thing3.value = @"goodbye";

    self.thing.next = thing3;
}

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"value did change for keyPath '%@'", keyPath);
    [self removeObserver:self forKeyPath:@"thing.next.value"];
    [self removeObserver:self forKeyPath:@"thing.next"];
}

@end
  

Я получаю этот результат…

2011-11-03 13:32:02.123 TestKVO[11637:707] значение изменилось для ключевого пути ‘thing.next’

2011-11-03 13:32:02.124 TestKVO[11637:707] Не удается удалить наблюдателя <NSKeyValueObservance 0x103828250> для пути ключа «next.value» из <Thing 0x10381d970>, скорее всего, потому, что значение для ключа «next» изменилось без отправки соответствующего уведомления KVO. Проверьте соответствие KVO классу Thing.

Ответ №1:

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

Вместо этого вы должны:

  • Регистрируйтесь только для @"thing.next"
  • Внутри метода обработки уведомлений отмените регистрацию для @"thing.next.value" старого @"thing.next" значения (найденного в change словаре), затем зарегистрируйтесь для @"thing.next.value" нового @"thing.next" значения (также найденного в change словаре).

Таким образом, привязки остаются неизменными при @"thing.next" изменении пути к ключу.

Комментарии:

1. Это, скорее всего, правильно. когда вы регистрируетесь thing.next.value , ваше приложение сохраняет слабую ссылку (т. Е. Не Сохраняется) на thing.next . Затем вы меняете thing.next так, что KVO запутывается, потому thing.next что изменилось, таким образом, изменилось thing.next.value , но уведомление о KVO не было отправлено для thing.next.value изменения. Тьфу. это может сбить с толку.

Ответ №2:

Пара вещей:

1) вы внедряете свои собственные сеттеры? если это так, вам нужно убедиться, что они соответствуют KVO, отправив willChangeValueForKey: и didChangeValueForKey: соответствующим образом. Найдите раздел о соответствии KVO в руководстве Apple по программированию с соблюдением ключевых значений.

РЕДАКТИРОВАТЬ: Вышесказанное было предназначено для проверки общего соответствия вашего класса. @sam правильно, что вам не нужны willChange... и didChange... в пользовательских установщиках, если вы не отключили автоматические уведомления.

2) вместо того, чтобы ваш объект наблюдал за его собственными свойствами (немного странно, ИМХО), вы могли бы реализовать пользовательские настройки, которые делают все, что вы хотите, в объекте при изменении значения.

Комментарии:

1. Я согласен с тем, что переопределение установщиков, вероятно, является лучшим маршрутом, но ему не нужно вызывать willChangeValueForKey: / didChangeValueForKey: . На самом деле, если бы он это сделал, его методы, вероятно, сработали бы дважды, потому что kvo работает автоматически, если вы его не отключите, даже если вы пишете свои собственные настройки. См . Соответствие требованиям KVO и automaticallyNotifiesObserversForKey: .

2. @sam прав в отношении автоматического KVO. Я имел в виду свой комментарий для проверки соответствия класса. Я отредактирую свой ответ.