Статьи

Наблюдение значения ключа с помощью Facebook KVOController

Если вы когда-либо работали с 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 не предоставляет способ проверить, наблюдает ли объект определенный путь ключа.

К счастью, команда Какао в 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, прежде чем использовать его в своих проектах, чтобы вы знали, что эта библиотека делает для вас за кулисами.