Как выполняют работу привязки?

Эта статья обеспечивает концептуальное объяснение того, как работает привязка Какао. Это описывает:

Необходимо ли уже быть знакомы с понятиями, представленными в том, Что такое Привязка Какао?.

Обзор технологий поддержки

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

Привязка какао полагается на другие технологии — кодирование значения ключа (KVC) и наблюдение значения ключа (KVO) — для передачи изменений между объектами, и на привязке значения ключа (KVB) для привязки значения в одном объекте к свойству в другом. Привязка какао также использует два протокола — NSEditor и NSEditorRegistration — что избавляются от справки, чтобы гарантировать, что любые незаконченные редактирования или отбрасываются или фиксируются перед элементами пользовательского интерфейса.

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

  Приложение рисования рисунка 1 В качестве примера

Реализация инспектора проиллюстрирована на рисунке 2. И текстовые поля и джойстик связываются с selection из NSArrayController. Контроллер contentArray связывается с массивом Графических объектов. Диаграмма имеет переменные экземпляра для представления угла ее тени в радианах и ее смещения от его центра.

  Привязка рисунка 2, например, графическое приложение

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

Полная последовательность событий, происходящая, когда пользователь редактирует значение в угловом текстовом поле, проиллюстрирована на рисунке 3.

Рисунок 3  полный цикл редактирования
  1. Пользователь вводит новое значение в текстовое поле Angle. Текстовое поле использует протокол NSEditorRegistration, чтобы указать, что редактирование началось, и когда это завершено. Обязательное текстовое поле указывает, обратимые радианы до градусов оценивают преобразователь, таким образом, новое значение преобразовывается в радианы.

  2. Используя KVC, через контроллер представление обновляет объект модели shadowAngle переменная.

  3. Через KVO модель сообщает контроллеру, что обновление было сделано к shadowAngle переменная.

  4. Через KVO контроллер сообщает джойстику и угловому текстовому полю, что обновление было сделано к его содержанию shadowAngle переменная.

Заметьте, что текстовое поле Offset не было вовлечено в цикл всегда. Привязка какао не налагает больше наверху, чем необходимо.

Следующие разделы объясняют более подробно, как установлена привязка и как работают базовые технологии.

Технологии поддержки подробно

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

Установление привязки с привязкой значения ключа

Привязка значения ключа используется для установления привязки. NSKeyValueBindingCreation неофициальный протокол объявляет, что методы устанавливают и удаляют привязку между объектами. Кроме того, это обеспечивает средние значения для класса для распространения привязки, которую это представляет.

В большинстве случаев необходимо использовать bind:toObject:withKeyPath:options:, и затем только, когда Вы устанавливаете привязку программно. Использование unbind: обсужден в Развязывании. Другие методы — метод класса exposeBinding: и методы экземпляра exposedBindings и valueClassForBinding:— полезны только в палитре Interface Builder.

NSEditor/NSEditorRegistration

Вместе NSEditorRegistration и протоколы NSEditor позволяют представлениям уведомлять контроллер, что редактирование в стадии реализации и гарантировать, что любые незаконченные редактирования фиксируются как и, когда необходимо.

Неофициальный протокол NSEditorRegistration реализован контроллерами для обеспечения интерфейса для представления — редактора — для информирования контроллера, когда это отменило фиксацию изменений. Когда редактирование инициируется, представление отправляет контроллер objectDidBeginEditing: сообщение. Когда редактирование завершено (например, когда пользователь нажимает Return), представление отправляет objectDidEndEditing: сообщение.

Контроллер ответственен за отслеживание, какие редакторы отменили фиксацию изменений и запросив, чтобы они фиксировали или отбросили любые незаконченные редактирования, когда надлежащий — например, если пользователь закрывает окно или выходит из приложения. Запрос принимает форму a commitEditing или discardEditing сообщение, определенное неофициальным протоколом NSEditor. NSController обеспечивает реализацию этого протокола, также, как и элементы пользовательского интерфейса Набора Приложения та привязка поддержки.

Кодирование значения ключа

Кодирование значения ключа (KVC) обеспечивает объединенный способ получить доступ к свойствам объекта по имени (ключ), не требуя использования пользовательских методов доступа. Протокол NSKeyValueCoding указывает среди других два метода, valueForKey: и setValue:forKey:, это предоставляет доступ к свойству объекта с указанным именем. Кроме того, методы setValue:forKeyPath: и valueForKeyPath: предоставьте доступ к свойствам через ключевые пути использования отношений формы relationship.property, например, content.lastName.

Привязка для данного свойства указывает объект и ключевой путь к свойству того объекта. Учитывая эту информацию, связанный объект может использовать кодирование значения ключа — в частности setValue:forKeyPath:— обновить объект, к которому это связывается, не имея необходимость с твердым кодом метод доступа, как проиллюстрировано на рисунке 4.

  Кодирование Значения ключа рисунка 4 в привязке Какао

Так как привязка Какао полагается на KVC, Ваша модель и объекты контроллера должны быть KVC-совместимыми для других объектов быть в состоянии связать с ними. Для поддержки KVC, просто необходимо следовать за рядом соглашений для именования методов доступа, кратко полученных в итоге здесь:

  • Для атрибута или к - называют одно отношение <key>, методы реализации называют <key> и, если атрибут является чтением-записью, set<Key>: — случай важен.

  • Для к - многие реализация отношения любой называют метод <key> или оба countOf<Key> и objectIn<Key>AtIndex:. Если связанные объекты не хранятся в массиве, последняя пара полезна. Вам решать для получения надлежащего значения для указанного индекса — не имеет значения, как получено значение. Для непостоянного к - многие отношение, необходимо также реализовать также set<Key> (если Вы реализовали <key>) или insertObject:in<Key>AtIndex: и removeObjectFrom<Key>AtIndex:.

Для полного изложения всех методов, объявленных протоколом NSKeyValueCoding, посмотрите, что Значение ключа Кодирует Руководство по программированию.

Наблюдение значения ключа

KVO является механизмом, позволяющим объекту зарегистрироваться для получения уведомлений об изменениях в значениях в других объектах. Для регистрации наблюдатель отправляет объект, который это хочет наблюдать addObserver:forKeyPath:options:context: сообщение, указывающее:

  • Объект, наблюдающий (очень часто self)

  • Ключевой путь для наблюдения (например, selection.name)

  • Какая информация будет отправлена на уведомлении (например, старое значение и новое значение)

  • Дополнительно, контекстная информация, которую передадут обратно на уведомлении (например, флаг для указания, какая привязка затрагивается),

Наблюдаемый объект передает изменения непосредственно в наблюдателях путем отправки им observeValueForKeyPath:ofObject:change:context: сообщение (определенный NSKeyValueObserving неофициальный протокол) — нет никакого независимого объекта notifier, сродни NSNotificationCenter. Рисунок 5 иллюстрирует, как изменение в значении модели (теневой угол) передается к представлениям с помощью KVO.

  Наблюдение Значения ключа рисунка 5 в привязке Какао

Сообщение отправляется наблюдателям для каждого изменения в наблюдаемом свойстве, независимо от того, как было инициировано изменение. (Обратите внимание на то, что сам протокол не делает предположений о том, что наблюдатель фактически делает с информацией.)

Один другой важный аспект KVO - то, что можно зарегистрировать значение ключа, как являющегося зависящим от значения одного или более других. Изменение в значении, связанном с одним из «основных» ключей, инициировало уведомление изменения для значения зависимого ключа. Например, границы получения графического объекта могут зависеть от смещения и угла связанной тени. Если должно быть уведомлено любое из тех изменений значений, какие-либо объекты, наблюдая границы получения.

Основное требование для использования KVO - то, что объекты модели KVO-совместимы. В большинстве случаев это фактически не требует никакого усилия — система во время выполнения добавляет поддержку автоматически. По умолчанию все вызовы методов KVC приводят к уведомлениям наблюдателя. Можно также реализовать ручную поддержку — дополнительную информацию см. в Значении ключа, Наблюдая Руководство по программированию.

Развязывание

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

При реализации пользовательского представления или контроллера с пользовательской привязкой необходимо гарантировать, что он очищает любую привязку, прежде чем он будет освобожден — в частности он должен выпустить любые объекты, сохраненные для указанной привязки в bind:toObject:withKeyPath:options: метод и должен вычеркнуть из списка как наблюдатель любых объектов, для которых он зарегистрировался как наблюдатель. Может быть удобно реализовать эту логику в unbind: метод, в котором Вы тогда вызываете как первый шаг dealloc.

Привязка более подробно

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

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

Можно установить привязку легко в Интерфейсном Разработчике, использующем область Bindings окна Info. В Интерфейсном Разработчике, angle привязка для джойстика, выводящего на экран смещение и угол тени графического объекта, могла бы быть похожей на рисунок 6.

  Привязка рисунка 6 для угла джойстика в Интерфейсном Разработчике

Установление этой привязки в Интерфейсном Разработчике эквивалентно программно отправке этого сообщения к джойстику:

[joystick bind:@"angle" toObject:GraphicController withKeyPath:@"selection.shadowAngle" options:options];

Параметры имеют следующие значения:

@"angle"

Идентифицирует атрибут, значение которого может быть связано со значением свойства в другом объекте. Обратите внимание на то, что обязательное имя не должно обязательно соответствовать имени фактической переменной экземпляра.

GraphicController

Объект, содержащий связанное - к свойству.

@"selection.shadowAngle"

Ключевой путь, идентифицирующий связанное - для свойства.

options

Словарь, указывающий любые опции, такие как заполнители, или в этом случае, преобразователь значения.

Информация, определенная параметрами, может храниться в связанном объекте (в этом случае джойстик) как переменные экземпляра, как обсуждено затем.

Этот пример, и те, которые следуют, предполагают, что джойстик представлен Джойстиком класса с переменными экземпляра, как определено в интерфейсе, показанном в Перечислении 1.

  Интерфейс перечисления 1 для класса Джойстика

@interface Joystick : NSView
{
    float angle;
    id observedObjectForAngle;
    NSString *observedKeyPathForAngle;
    NSValueTransformer *angleValueTransformer;
    // ...
}

В bind:toObject:withKeyPath:options: метод объект должен как минимум, делает следующее:

Пример кода в Перечислении 2 показывает частичную реализацию Джойстика bind:toObject:withKeyPath:options: метод, имеющий дело только с angle привязка.

Перечисление 2  Частичная реализация bind:toObject:withKeyPath:options метода для класса Джойстика

static void *AngleBindingContext = (void *)@"JoystickAngle";
 
- (void)bind:(NSString *)binding
 toObject:(id)observableObject
 withKeyPath:(NSString *)keyPath
 options:(NSDictionary *)options
{
 // Observe the observableObject for changes -- note, pass binding identifier
 // as the context, so you get that back in observeValueForKeyPath:...
 // This way you can easily determine what needs to be updated.
 
if ([binding isEqualToString:@"angle"])
 {
    [observableObject addObserver:self
                   forKeyPath:keyPath
                  options:0
                  context:AngleBindingContext];
 
    // Register what object and what keypath are
    // associated with this binding
    observedObjectForAngle = [observableObject retain];
    observedKeyPathForAngle = [keyPath copy];
 
    // Record the value transformer, if there is one
    angleValueTransformer = nil;
    NSString *vtName = [options objectForKey:@"NSValueTransformerName"];
    if (vtName != nil)
    {
        angleValueTransformer = [NSValueTransformer
            valueTransformerForName:vtName];
    }
 }
 // Implementation continues...

Эта частичная реализация не записывает параметры привязки кроме преобразователя значения (несмотря на то, что может просто случиться так, что привязка не допускает их). Это, тем не менее, иллюстрирует основные принципы установления привязки. Заметьте в частности, что контекстная информация передала в addObserver:forKeyPath:options:context: сообщение; это возвращается в observeValueForKeyPath:ofObject:change:context: метод и может использоваться для определения, какая привязка затронута обновлением значения.

Реакция на изменения

Как отмечено ранее, существует два аспекта к управлению изменениями — реакция на инициируемые в представление изменения, которые должны быть распространены в конечном счете к модели и реакции на инициируемые в модель изменения, которые должны быть отражены в представлении. Этот раздел иллюстрирует обоих и показывает, как KVC и KVO играют их роли.

Инициируемые в представление обновления

Вспомните, что джойстик был связан с контроллером со следующим методом:

[joystick bind:@"angle" toObject:GraphicController withKeyPath:@"selection.shadowAngle" options:options];

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

bind: @"angle"

Если независимо от того, что связано с angle изменения,

toObject: GraphicController

скажите указанный объект (GraphicController) это

withKeyPath: @"selection.shadowAngle"

значение (GraphicController) selection.shadowAngle изменился

options: options

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

Если значение связалось с angle изменения — обычно, когда пользователь щелкает или перетаскивает мышь в представлении — джойстик, должны передать новое значение контроллеру с помощью KVC, как проиллюстрировано на рисунке 4. Джойстик должен поэтому реагировать на ввод данных пользователем следующим образом:

  • Определите новые значения для угла и сместите

  • Обновите его собственный дисплей как надлежащий

  • Передайте новые значения к контроллеру, с которым это связывается

Перечисление 3 показывает частичную реализацию метода обновления для класса Джойстика. Выборка имеет дело только с angle привязка. Это иллюстрирует использование кодирования значения ключа для передачи нового значения (преобразованный преобразователем значения если надлежащий) к наблюдаемому объекту.

  Метод Обновления перечисления 3 для класса Джойстика

-(void)updateForMouseEvent:(NSEvent *)event
{
    float newAngleDegrees;
    // calculate newAngleDegrees...
 
    [self setAngle:newAngleDegrees];
 
    if (observedObjectForAngle != nil)
    {
        NSNumber *newControllerAngle = nil;
 
        if (angleValueTransformer != nil)
        {
            newControllerAngle =
                [angleValueTransformer reverseTransformedValue:
                    [NSNumber numberWithFloat:newAngleDegrees]];
        }
        else
        {
            newControllerAngle = [NSNumber numberWithFloat:newAngleDegrees];
        }
        [observedObjectForAngle setValue: newControllerAngle
                  forKeyPath: observedKeyPathForAngle];
    }
    // ...
}

Обратите внимание на то, что этот пример опускает несколько важных подробных данных, таких как регистрация редактора и проверяя, что преобразователь значения позволяет обратные трансформации.

Инициируемые в модель обновления

Вспомните снова, что джойстик был связан с контроллером со следующим методом:

[joystick bind:@"angle" toObject:GraphicController withKeyPath:@"selection.shadowAngle" options:options];

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

toObject: GraphicController

Если GraphicController

withKeyPath:@"selection.shadowAngle"

selection.shadowAngle изменения

bind:@"angle"

обновите то, что связано с представленным angle ключ

options:options

использование указанных опций (например, использование преобразователя значения).

Получатель поэтому зарегистрировался как наблюдатель ключевого пути указанного объекта (selection.shadowAngle) в bind:toObject:withKeyPath:options: метод, как был показан в Перечислении 2. Наблюдаемые объекты уведомляют своих наблюдателей путем отправки им observeValueForKeyPath:ofObject:change:context: сообщение. Перечисление 4 показывает частичную реализацию для класса Джойстика для обработки уведомлений наблюдателя тот результат.

Фундаментальное требование observeValueForKeyPath:ofObject:change:context: метод - то, что обновляется значение, связанное с соответствующим атрибутом. Эта выборка также показывает, как она может получить информацию о заполнителе, которая могла бы использоваться в методе дисплея, чтобы дать визуальную обратную связь пользователю в этом случае с помощью переменной экземпляра, указывающей, что по некоторым причинам угол «плох».

  Метод Наблюдения перечисления 4 для класса Джойстика

- (void)observeValueForKeyPath:(NSString *)keyPath
              ofObject:(id)object
            change:(NSDictionary *)change
               context:(void *)context
{
    // You passed the binding identifier as the context when registering
    // as an observer--use that to decide what to update...
 
    if (context == AngleObservationContext)
    {
        id newAngle = [observedObjectForAngle
            valueForKeyPath:observedKeyPathForAngle];
        if ((newAngle == NSNoSelectionMarker) ||
            (newAngle == NSNotApplicableMarker) ||
            (newAngle == NSMultipleValuesMarker))
        {
            badSelectionForAngle = YES;
        }
        else
        {
            badSelectionForAngle = NO;
            if (angleValueTransformer != nil)
            {
                newAngle = [angleValueTransformer
                    transformedValue:newAngle];
            }
            [self setValue:newAngle forKey:@"angle"];
        }
    }
    // ...
 
    [self setNeedsDisplay:YES];
}

В большинстве средств управления метод дисплея изменяет визуальное представление в зависимости от текущего выбора.