Вступление
Если вы когда-либо работали с KVO (Key-Value Observing) в Какао, есть вероятность, что вы столкнулись с различными проблемами. API не велик, и забывание удалить наблюдателя может привести к утечкам памяти или, что еще хуже, к сбоям. Библиотека KVOController от Facebook направлена на решение этой проблемы.
Что такое наблюдение значения ключа?
Если вы новичок в наблюдении значения ключа или KVO, я рекомендую сначала прочитать руководство разработчика Apple по этой теме или статью Мэтта Томпсона о NSHipster . Цитируя руководство Apple по KVO, «Наблюдение ключ-значение обеспечивает механизм, который позволяет объектам получать уведомления об изменениях определенных свойств других объектов». Мэтт определяет KVO следующим образом: «Наблюдение значения ключа позволяет проводить произвольный, четный самоанализ между конкретными экземплярами объекта путем прослушивания изменений в определенном ключевом пути». Ключевые слова выровнены и ключевой путь ,
Прежде чем мы обсудим библиотеку KVOController, я хотел бы немного поговорить об API KVO.
Добавление наблюдателя
Я не буду подробно рассказывать о KVO в этом руководстве, но важно, чтобы вы поняли основную концепцию KVO. Идея проста. Объект может прослушивать изменения определенных свойств другого объекта. Объект наблюдения добавляется целевым объектом в качестве наблюдателя для определенного ключевого пути.
Давайте проиллюстрируем это на примере. Если objectB
желает получать уведомление при изменении свойства name
для objectA
, то objectA
необходимо добавить objectB
в качестве наблюдателя для имени пути ключа. Благодаря многословности Objective-C, код для этого довольно прост.
1
|
[objectA addObserver:objectB forKeyPath:@»name» options:NSKeyValueObservingOptionNew context:NULL];
|
Отвечая на изменения
Всякий раз, когда objectA
name
объекта observeValueForKeyPath:ofObject:change:context:
изменяется, observeValueForKeyPath:ofObject:change:context:
вызывается. Первый параметр — это ключевой путь, который наблюдается objectB
, второй параметр — это объект ключевого пути, третий аргумент — это словарь, описывающий изменения, а последний аргумент — это контекст, который был передан как последний аргумент addObserver:forKeyPath:options:context:
Я надеюсь, вы согласны, что это не очень элегантно. Если вы широко используете KVO, реализация наблюдайте за ValveForKeyPath: ofObject: change: context: быстро становится длинным и сложным.
Удаление наблюдателя
Важно прекратить прослушивание изменений, когда объект больше не заинтересован в получении уведомлений для определенного ключевого пути. Это делается путем вызова removeObserver:forKeyPath:
или removeObserver:forKeyPath:context:
Проблема, с которой каждый разработчик сталкивается в какой-то момент, заключается в том, что он либо забывает вызвать removeObserver:forKeyPath:
либо вызывает removeObserver:forKeyPath:
с путем ключа, который не наблюдается наблюдателем. Причин для этого много, и они являются корнем проблемы, с которой сталкиваются многие разработчики при работе с KVO.
Если вы забудете вызвать removeObserver:forKeyPath:
вы можете получить утечку памяти. Если вы вызываете removeObserver:forKeyPath:
и объект не зарегистрирован как наблюдатель, removeObserver:forKeyPath:
исключение. Суть в том, что протокол NSKeyValueObserving
не предоставляет способ проверить, наблюдает ли объект определенный путь ключа.
KVOController на помощь
К счастью, команда Какао в Facebook была так же раздражена вышеуказанными проблемами, как и вы, и они нашли решение — библиотеку KVOController . Вместо того, чтобы изобретать велосипед, команда в Facebook решила построить поверх KVO. Несмотря на свои недостатки, KVO является надежным, широко поддерживаемым и очень полезным.
Библиотека KVOController добавляет в KVO несколько вещей:
- потокобезопасность
- безболезненное удаление наблюдателей
- поддержка блоков и пользовательских действий
Требования
Прежде чем начать, важно подчеркнуть, что для библиотеки KVOController требуется ARC и что минимальные цели развертывания — iOS 6 для iOS и 10.7 для OS X.
Установка
Я большой сторонник CocoaPods, и я надеюсь, что вы тоже. Чтобы добавить библиотеку KVOController в проект с использованием CocoaPods, добавьте модуль pod в Podfile вашего проекта и запустите pod update
для установки библиотеки.
1
|
pod ‘KVOController’
|
Кроме того, вы можете скачать последнюю версию библиотеки с GitHub и вручную добавить библиотеку, скопировав KVOController.h и KVOController.m в ваш проект.
Примеры
инициализация
Первое, что вам нужно сделать, это инициализировать экземпляр класса FBKVOController
. Взгляните на следующий фрагмент кода, в котором я создаю экземпляр FBKVOController
в контроллере представления initWithNibName:bundle:
метод.
1
2
3
4
5
6
7
8
9
|
— (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_KVOController = [FBKVOController controllerWithObserver:self];
}
return self;
}
|
Обратите внимание, что я храню ссылку на объект _KVOController
переменной экземпляра _KVOController
контроллера _KVOController
. Отличительной особенностью библиотеки KVOController является то, что наблюдатель автоматически удаляется в момент FBKVOController
объекта FBKVOController
. Другими словами, не нужно помнить, чтобы удалить наблюдателя, поскольку это происходит автоматически в тот момент, FBKVOController
объект FBKVOController
освобождается.
Добавление наблюдателя
У вас есть несколько вариантов начать наблюдение за объектом. Вы можете воспользоваться традиционным подходом, вызвав observe:keyPath:options:context:
Результатом является то, что observeValueForKeyPath:ofObject:change:context:
каждый раз, когда происходит событие изменения.
1
|
[_KVOController observe:person keyPath:@»name» options:NSKeyValueObservingOptionNew context:NULL];
|
Однако класс FBKVOController
также использует блоки и настраиваемые действия, как показано в следующих фрагментах кода. Я уверен, что вы согласны, что это делает работу с KVO намного более приятной.
1
2
3
|
[_KVOController observe:person keyPath:@»name» options:NSKeyValueObservingOptionNew block:^(id observer, id object, NSDictionary *change) {
// Respond to Changes
}];
|
1
|
[_KVOController observe:person keyPath:@»name» options:NSKeyValueObservingOptionNew action:@selector(nameDidChange:)];
|
Удаление наблюдателя
Даже если наблюдатель автоматически удаляется, когда объект FBKVOController
освобождается, часто необходимо прекратить наблюдение объекта до того, как наблюдатель был освобожден. Библиотека KVOController имеет ряд методов для выполнения этой простой задачи.
Чтобы прекратить наблюдать определенный путь ключа объекта, вызовите unobserve:keyPath:
и передайте путь объекта и ключа. Вы также можете остановить наблюдение за объектом, вызвав unobserve:
и передать объект, который вы хотите прекратить наблюдать. Чтобы прекратить наблюдение за каждым объектом, вы можете отправить объекту FBKVOController
сообщение FBKVOController
.
Без исключений
Если вы посмотрите на реализацию класса FBKVOController
, вы заметите, что он хранит внутреннюю карту объектов и основных путей, которые наблюдает наблюдатель. Класс FBKVOController
более простителен, чем реализация FBKVOController
от Apple. Если вы скажете объекту FBKVOController
прекратить наблюдение объекта или пути ключа, который не наблюдается, исключение не выдается. Так и должно быть.
Вывод
Несмотря на то, что KVO не является сложной для понимания концепцией, при работе с KVO реальная проблема заключается в том, чтобы наблюдатели были должным образом удалены и условия гонки не приводили к хаосу.
Я рекомендую вам взглянуть на библиотеку KVOController . Однако я также советую вам лучше понять KVO, прежде чем использовать его в своих проектах, чтобы вы знали, что эта библиотека делает для вас за кулисами.