2015-01-27 2 views
1

Предположим, я хочу наблюдать свойство с именем 'isEnabled' в свойстве с именем 'controller' на себе. AFAIK у меня есть два варианта установки такого рода наблюдения:

1. [self.controller addObserver:self forKeyPath:@"isEnabled" options:0 context:nil];
2. [self addObserver:self forKeyPath:@"controller.isEnabled" options:0 context:nil];

Использование вложенных путей ключа в KVO

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

Мой вопрос в том, где, черт возьми, это документировано, если вообще? Я знаю, что это работает, но я должен использовать его?
Я не мог найти упоминания о таком поведении в документах Apple, хотя некоторые другие парни упомянули об этом на форумах. Любая ссылка будет с радостью принята.

Спасибо.

+0

Я почти считаю неявно документированы для принятия ключа * путь * в отличие от просто клавишу * *. –

ответ

1

Это не просто, что вы получите уведомление об изменении, если controller изменения свойств, то, что КВО переключится на отслеживание isEnabled свойства нового контроллера и остановить отслеживание isEnabled свойства старого контроллера.

Это подразумевается в понятии ключевого пути. Наблюдение за ключевыми значениями построено на основе кодирования по значению ключа. Key-Value Coding Руководство по программированию говорит это о ключевых путей в Key-Value Coding Fundamentals:

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

Например, ключ путь address.street бы получить значение address имущества от принимающего объекта, а затем определить street свойства по отношению к address объекта.

Значение -addObserver:forKeyPath:options:context: не означает «следовать по пути ключа к конечному элементу и наблюдать это свойство на втором-последнем объекте». Это «наблюдать за этим ключом пути объекта-получателя». Путь ключа всегда рассматривается начиная с приемника.

Иными словами, для вашего второго примера кода это не «наблюдать за свойством isEnabledcontroller от self» (вот в чем смысл вашего первого примера). Смысл «наблюдать за controller.isEnabled от self. Каждый раз, когда результат оценки выражения [self valueForKeyPath:@"controller.isEnabled"] изменился или мог быть изменен, уведомите меня».

Я знаю, что это работает, но я должен использовать его?

Да, вы должны использовать его. Это подразумеваемый смысл API. Именно поэтому метод описывает свой параметр как ключ путь, а не только ключ.

+0

_ «Дело не только в том, что вы получите уведомление об изменении, если изменяется свойство контроллера, это означает, что KVO переключится на отслеживание свойства isEnabled нового контроллера и прекратит отслеживать свойство isEnabled старого контроллера« _. Спасибо за разъяснение этого вопроса, однако я все еще чувствую, что неудобно с тем, что это нигде не документировано. –

0

на втором подходе я получит уведомление, если «контроллер» объект на себя был заменен

Я бы перефразировать, сказав, что на втором подходе вы получите уведомление, если контроллер объект получает замену или его имущество isEnabled изменяется. Другими словами, когда изменяется controller.isEnabled (как объяснил ответ Кена).

Проверить этот пример:

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    self.controller = [[ViewController2 alloc] init]; 

    [self.controller addObserver:self forKeyPath:@"isEnabled" options:NSKeyValueObservingOptionNew context:nil]; 
    [self addObserver:self forKeyPath:@"controller.isEnabled" options:NSKeyValueObservingOptionNew context:nil]; 

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     self.controller.isEnabled = !self.controller.isEnabled; 

     // replace controller 
     [self.controller removeObserver:self forKeyPath:@"isEnabled"]; 
     self.controller = [[ViewController2 alloc] init]; 
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
      self.controller.isEnabled = !self.controller.isEnabled; 
     }); 
    }); 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    NSLog(@"KVO %d %@ %p", self.controller.isEnabled, keyPath, self.controller); 
} 

Мы получим 4 КВО уведомления:

KVO 1 controller.isEnabled 0x7fbbc2e4b4e0 <-- These 2 fire together when we toggle isEnbled 
KVO 1 isEnabled 0x7fbbc2e4b4e0   <-- So basically 1. and 2. behave the same 
KVO 0 controller.isEnabled 0x7fbbc2e58d30 <---- controller was replaced 
KVO 1 controller.isEnabled 0x7fbbc2e58d30 <---- Toggle on the new instance 
Смежные вопросы