Образец регистратора

Шаблон разработки Регистратора решает общую проблему перенаправления события, происходящего в одном контексте выполнения приложения к другому контексту выполнения для обработки. Это - гибридный образец. Несмотря на то, что это не появляется в книге «Gang of Four», это комбинирует элементы Команды, Уведомления и шаблонов разработки Прокси, описанных в той книге. Это - также вариант образца Батута (который также не появляется в книге); в этом образце событие первоначально получено объектом батута, так называемым, потому что это сразу возвращается, или перенаправления, событие к целевому объекту для обработки.

Шаблон разработки регистратора на практике

Уведомление KVO вызывает observeValueForKeyPath:ofObject:change:context: метод реализован наблюдателем. Если изменение в свойстве происходит на вторичном потоке, observeValueForKeyPath:ofObject:change:context: код выполняется на том же самом потоке. Там центральный объект в этом образце, регистраторе, действует как посредник потока. Поскольку рисунок 11-1 иллюстрирует, объект регистратора присваивается как наблюдатель свойства объекта модели. Реализации регистратора observeValueForKeyPath:ofObject:change:context: перенаправить уведомление, полученное на вторичном потоке к другому контексту выполнения — основная очередь работы, в этом случае. Когда свойство изменяется, регистратор получает уведомление KVO. Регистратор сразу добавляет блочную операцию к основной очереди работы; блок содержит код — указанный клиентом — который обновляет пользовательский интерфейс соответственно.

Рисунок 11-1  , Возвращающий KVO, обновляет основной очереди работы

Вы определяете класс регистратора так, чтобы он имел элементы, которые он должен добавить сам как наблюдатель свойства и затем преобразовать уведомление KVO в задачу обновления. Таким образом это должно знать то, что возражает, что это наблюдает, свойство объекта, который это наблюдает, что задача обновления выполниться, и что очередь выполнить его на. Перечисление 11-1 показывает начальное объявление RCReceptionist класс и его переменные экземпляра.

Перечисление 11-1  , Объявляющее класс регистратора

@interface RCReceptionist : NSObject {
    id observedObject;
    NSString *observedKeyPath;
    RCTaskBlock task;
    NSOperationQueue *queue;
}

RCTaskBlock переменная экземпляра является блочным объектом следующего заявленного типа:

typedef void (^RCTaskBlock)(NSString *keyPath, id object, NSDictionary *change);

Эти параметры подобны тем observeValueForKeyPath:ofObject:change:context: метод. Затем, класс параметра объявляет метод фабрики единого класса в который RCTaskBlock объект является параметром:

+ (id)receptionistForKeyPath:(NSString *)path
        object:(id)obj
         queue:(NSOperationQueue *)queue
          task:(RCTaskBlock)task;

Это реализует этот метод, чтобы присвоить переданный - в значении к переменным экземпляра создаваемого объекта регистратора и добавить что объект как наблюдатель свойства объекта модели, как показано в Перечислении 11-2.

Перечисление 11-2  метод фабрики классов для создания объекта регистратора

+ (id)receptionistForKeyPath:(NSString *)path object:(id)obj queue:(NSOperationQueue *)queue task:(RCTaskBlock)task {
    RCReceptionist *receptionist = [RCReceptionist new];
    receptionist->task = [task copy];
    receptionist->observedKeyPath = [path copy];
    receptionist->observedObject = [obj retain];
    receptionist->queue = [queue retain];
    [obj addObserver:receptionist forKeyPath:path
             options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:0];
    return [receptionist autorelease];
}

Обратите внимание на то, что код копирует блочный объект вместо того, чтобы сохранить его. Поскольку блок, вероятно, создавался на штабеле, он должен быть скопирован в «кучу», таким образом, он существует в памяти, когда поставлено уведомление KVO.

Наконец, класс параметра реализует observeValueForKeyPath:ofObject:change:context: метод. Реализация (см. Перечисление 11-3) проста.

Перечисление 11-3  , Обрабатывающее уведомление KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
        change:(NSDictionary *)change context:(void *)context {
    [queue addOperationWithBlock:^{
        task(keyPath, object, change);
    }];
}

Этот код просто ставит в очередь задачу на данную очередь работы, передавание задачи блокирует наблюдаемый объект, ключевой путь для измененного свойства и словарь, содержащий новое значение. Задача инкапсулируется в NSBlockOperation объект, выполняющий задачу на очереди.

Объект клиента предоставляет блочный код, обновляющий пользовательский интерфейс, когда это создает объект регистратора, как показано в Перечислении 11-4. Обратите внимание на то, что, когда это создает объект регистратора, клиентские передачи в очереди работы, на которой блок должен быть выполнен, в этом случае основная очередь работы.

Перечисление 11-4  , Создающее объект регистратора

        RCReceptionist *receptionist = [RCReceptionist receptionistForKeyPath:@"value" object:model queue:mainQueue task:^(NSString *keyPath, id object, NSDictionary *change) {
            NSView *viewForModel = [modelToViewMap objectForKey:model];
            NSColor *newColor = [change objectForKey:NSKeyValueChangeNewKey];
            [[[viewForModel subviews] objectAtIndex:0] setFillColor:newColor];
        }];

Когда использовать образец регистратора

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

Одна общая ситуация, где образец Регистратора полезен, является наблюдением значения ключа. В наблюдении значения ключа изменения в значении свойства объекта модели передаются наблюдателям через уведомления KVO. Однако изменения в объекте модели могут произойти на фоновом потоке. Это приводит к несоответствию потока, потому что изменения в состоянии объекта модели обычно приводят к обновлениям к пользовательскому интерфейсу, и они должны произойти на основном потоке. В этом случае Вы хотите перенаправить уведомления KVO основному потоку. где могут произойти обновления к пользовательскому интерфейсу приложения.