Шаблон делегата в среде Cocoa, обязанности которого описываются в протоколе, можно вполне сымитировать в прикладном коде.
Несмотря на то что для настройки этого шаблона требуется некоторый практический опыт и определенное время, такой подход зачастую оказывается наиболее подходящим, поскольку это позволяет оптимально распределить знания и ответственность среди различных вовлеченных объектов. Характерным тому примером служит двухсторонняя связь, когда объект А создает и настраивает объект В, а перед тем, как объект В прекратит свое существование, он устанавливает обратную связь с объектом А.
Действие шаблона в данном примере состоит в том, что в заголовочном файле класса В определяется протокол SomeProtocol наряду со свойством delegate, обозначаемым как id <SomeProtocol>. Таким образом, класс А импортирует заголовочный файл из класса В и настраивает его, что вполне приемлемо и правильно, поскольку объект А собирается создать и настроить объект В. Однако классу В совсем не обязательно знать о классе А, что также приемлемо и правильно, поскольку его основная обязанность — лишь обслуживать любой другой объект. Поскольку класс А импортирует заголовочный файл из класса В, то ему известно о протоколе SomeProtocol, и он может принять и реализовать его обязательные методы. Когда объект А создает объект В, он также устанавливает себя в качестве делегата объекта В. Теперь объекту В известно все, что он должен знать, а именно: он может посылать сообщения по протоколу SomeProtocol своему делегату независимо от того, к какому именно классу принадлежит этот делегат.
Для того чтобы стало понятнее, почему в данном случае подходит именно такой шаблон, обратимся к конкретному примеру. В одном из моих приложений отображается представление, где пользователь может перемещать три ползунка для выбора цвета. Соответственно код этого представления находится в классе ColorPickerController. Когда пользователь нажимает кнопку Done или Cancel, представление должно быть удалено с экрана, но прежде в коде, отображающем это представление, нужно выяснить, какой именно цвет выбрал пользователь. Для этого нужно послать сообщение от экземпляра класса ColorPickerController обратно экземпляру класса, отображающего представление. Ниже приведено объявление этого сообщения.
Невольно возникает вопрос: где должно быть размещено это объявление? В моем приложении экземпляр, который, по существу, отображает представление типа ColorPicker Controller, относится к классу SettingsController. Поэтому приведенный выше метод можно было бы объявить в разделе интерфейса заголовочного файла класса SettingsController и затем организовать импорт этого файла в классе Color PickerController. Однако такой подход оказывается неверным по следующим причинам.
Следовательно, требуется, чтобы в классе ColorPickerController был объявлен метод, который должен вызываться из этого же класса, а также отправлять вслепую сообщение некоторому получателю без учета принадлежности последнего к какому-то конкретному классу Для этого потребуется установить фактическую связь между объявлением данного метода в классе ColorPickerController и его реализацией в классе получателя. Именно такую связь и устанавливает протокол! Итак, в заголовочном файле класса ColorPickerController следует определить протокол вместе с входящим в него данным методом, а в классе, отображающем представление типа ColorPickerController, — установить соответствие этому протоколу. Кроме того, в классе ColorPickerController имеется свойство, обозначаемое как delegate или аналогичным образом. Этим обеспечивается канал связи для передачи сообщения, а компилятор уведомляется, что отправка данного сообщения является вполне допустимой.
Ниже приведено содержимое заголовочного файла ColorPickerController. Обратите внимание на применение предваряющего объявления, упоминавшегося в главе 10.
Когда в экземпляре класса SettingsController создается и настраивается экземпляр класса ColorPickerController, то он устанавливает себя также в качестве делегата для экземпляра класса ColorPickerController. Если теперь пользователь выберет цвет, то
экземпляру класса ColorPickerController станет известно, куда нужно отправить сообщение colorPicker:didSetColorNamed: toColor:, а именно: его делегату. Компилятор позволит это сделать, поскольку делегат принял протокол ColorPickerDelegate, как показано ниже.
Если создать в среде Xcode проект из шаблона Utility Application, то можно обнаружить, что и в нем воплощается та же самая архитектура. Этот шаблон начинается с класса MainViewController, а завершается созданием класса FlipsideViewController. Когда объект типа FlipsideViewController готов прекратить свое существование, ему требуется отправить сообщение flipsideViewControllerDidFinish: обратно тому объекту, который его создал. Таким образом, в классе FlipsideViewController определяется протокол FlipsideViewControllerDelegate, требующий реализации метода flipsideVie wControllerDidFinish: наряду со свойством delegate, обозначаемым как id <Flipsi deViewControllerDelegated
Когда экземпляр класса MainViewController создает экземпляр класса Flipside ViewController, он устанавливает себя в качестве делегата для экземпляра класса FlipsideViewController. Он действительно может это сделать, поскольку в классе MainViewController принят протокол FlipsideViewControllerDelegate! Задача решена, и дело с концом. Если же у вас возникнут сомнения по поводу настройки шаблона делегата и протокола, создайте проект по шаблону Utility Application и тщательно изучите его.