Кодирование ключ-значение позволяет вам извлекать или изменять свойство объекта, используя строку, во время выполнения, вместо необходимости писать код, который компилируется в фиксированное свойство с самого начала:
NSNumber* foo = [myPopup valueForKey: @"selectedItemIndex"];
[myPopup setValue: @15 forKey: @"selectedItemIndex"];
Хорошим примером для этого является NSTableView на Mac, где вы можете просто установить идентификатор для каждого столбца таблицы, который соответствует свойству объекта модели, который он должен отображать, а затем ваш источник данных просто вызывает -valueForKey: / - setValue: forKey: с идентификатором столбца в качестве ключа и значения в значительной степени отображаются / устанавливаются сами. Вы просто добавляете правильные столбцы в табличное представление в XIB.
Наблюдение за значением ключа было добавлено позже и позволяет вам регистрироваться, чтобы получать уведомления об изменениях, внесенных в другой объект. Вы регистрируете свой интерес, выполнив:
void* gMyKVOContext = &gMyKVOContext; // global variable somewhere that guarantees us a unique address that doesn't collide with a subclass's registration for observing the same property
...
[interestingObject addObserver: interestedObject forKeyPath: @"interestingProperty" options: 0 context: gMyKVOContext];
Всякий раз, когда это свойство изменяется, -observeValueForKeyPath: ofObject: change: context: будет вызываться для объекта, который вы указали в качестве наблюдателя. Таким образом, вы бы реализовали это как:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if( context == gMyKVOContext && [keyPath isEqualToString: @"interestingProperty"] )
{
// Update UI that shows interestingProperty
}
else
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
}
Преимущество здесь в том, что вы получаете живой в тот момент, когда изменяется другое свойство. Обратите внимание, что объекты должны выполнять небольшую работу, поэтому эти уведомления отправляются, поэтому не все свойства являются наблюдаемыми по значению ключа. Также обратите внимание, что некоторые объекты могут находиться в недопустимом состоянии, если два связанных свойства изменяются сразу после другого: вы получаете уведомление после изменения первого свойства, которое теперь противоречит второму, и только тогда второе свойство изменяется, и вы ' повторно уведомлен об этом. Поэтому во время первого обратного вызова наблюдателя объект может находиться в странном состоянии, поэтому будьте осторожны, как вы реагируете на это.
Чтобы сделать свойство наблюдаемым, либо используйте реализацию @synthesized по умолчанию, когда вы определяете его, либо, если вы определяете его самостоятельно, реализуйте установщик следующим образом:
-(void) setFoo: (int)inFoo
{
[self willChangeValueForKey: @"foo"];
_foo = inFoo;
[self didChangeValueForKey: @"foo"];
}
Тогда всегда проходите через установщик, чтобы изменить его, не меняйте _foo напрямую. Если у вас есть связанные свойства, которые могут противоречить друг другу, как указано выше, хороший способ избежать этого - всегда менять их оба парами (однако, тогда вы не можете использовать KVC). Для этого реализуем комбинированный сеттер, например:
-(void) setFoo: (int)inFoo bar: (int)inBar
{
[self willChangeValueForKey: @"foo"];
[self willChangeValueForKey: @"bar"];
_foo = inFoo;
_bar = inBar;
[self didChangeValueForKey: @"bar"];
[self didChangeValueForKey: @"foo"];
}
Таким образом, оба уведомления отправляются, когда свойства находятся в надлежащем состоянии.