Немного запутался в делегатах в Objective-C

Я хорошо знаю Objective-C и работаю над книгой о SDK для iPhone (взятой из книги Obj-C, в которой только что были консольные программы). Он попытался объяснить делегатам, хотя был в спешке и не совсем понимал, что он пытался передать. Я немного не понимаю, что это такое и когда вы их используете.

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

Кто-нибудь хочет уточнить?

Спасибо!


person Community    schedule 28.08.2009    source источник
comment
Мне было нелегко с этим, когда я начинал смотреть на Какао. Ты не одинок.   -  person jergason    schedule 29.08.2009


Ответы (6)


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

Какао делает обратное; вместо этого экземпляры запрашивают данные, когда и если они необходимы.

Существует четыре основных типа методов делегата:

  • Условно до - сигнализирует о том, что что-то должно произойти, но делегат может прервать выполнение. В названии всегда должно быть слово "Должен".
    Пример: searchBar Should EndEditing:.
  • Безоговорочно до - сигнализирует о том, что что-то должно произойти. В названии всегда должно быть слово будет.
    Пример: application Will Terminate:.
  • Безоговорочно после - сигнализирует о том, что что-то произошло. В названии всегда есть слово "сделал".
    Пример: application Did FinishLaunching:.
  • Настройщики - запрашивайте информацию о том, как работать. Название включает в себя необходимую информацию.
    Пример tableView: viewForHeaderInSection :.

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

person PeyloW    schedule 28.08.2009
comment
Безусловно, лучшее объяснение среди этих ответов, имхо - person T .; 29.08.2009

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

Чтобы класс был делегатом, вы должны объявить его таковым. В приведенном выше примере предположим, что у вас есть объект ApplicationController, который управляет потоком вашего приложения. В декларации мы бы сказали

@interface ApplicationController : NSObject <UIAlertViewDelegate>

Это сообщает компилятору, что ApplicationController собирается реализовать некоторые методы в протоколе UIAlertViewDelegate. Мы ищем этот протокол в нашей документации и видим список методов. Поскольку мы хотим что-то сделать при нажатии кнопки, мы видим:

alertView:clickedButtonAtIndex: - отправляется делегату, когда пользователь нажимает кнопку в представлении предупреждений. Этот метод не является обязательным.

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

Итак, если мы реализуем в ApplicationController метод с именем alertView:clickedButtonAtIndex, создадим объект ApplicationController, а затем установим этот объект в качестве делегата показываемого нами предупреждения, все будет настроено. Как только кто-то нажимает кнопку в предупреждении, вызывается метод alertView:clickedButtonAtIndex, передавая alertView и индекс нажатой кнопки.

Это позволяет вам делать с этой информацией все, что угодно. Простой оператор case:

if( buttonIndex == 0 ) {
    _myString = @"Pressed the button";
} else {
    _myString = @"Pressed the other button";
}

Справочная документация по Objective-C очень и очень хороша, а все протоколы делегирования говорят сами за себя.

person johnw188    schedule 28.08.2009
comment
что означает символ at? - person Stefano Borini; 29.08.2009
comment
Это зависит от того, о чем вы говорили. Вы можете определить строки на месте, сказав здесь @string, что является объективной синтаксической вещью. В противном случае @interface - это объявление класса. Многие вещи, специфичные для цели c, начинаются с символа @, поскольку он никогда не используется в идентификаторах C / C ++. Это позволяет разработчикам цели c гарантировать отсутствие путаницы в отношении того, что что-то из C / C ++ или цель c. - person johnw188; 29.08.2009

Хороший способ понять делегатов - это на примере. Один из примеров - UITableView (или NSTableView, в зависимости от того, говорим ли мы об iPhone или Mac OS). В любом случае табличное представление имеет delegate и dataSource (оба действуют как вспомогательные объекты для получателя).

Вместо того, чтобы UITableView обрабатывать события, когда, например, пользователь нажимает на одну из его строк, он вместо этого сообщает своему delegate «Эй! Меня коснулись этой строки и этого раздела, делайте, что хотите!». Обычно delegate - это своего рода Контроллер, который реализует правильный метод. Таким образом, табличное представление (после проверки, действительно ли delegate имеет определение метода) отправляет такое сообщение:

[delegate tableView:self didSelectRowAtIndexPath:indexPath];

Поскольку ваш контроллер является делегатом таблицы и реализует этот метод, он решает, что делать. Когда метод завершается (в этом случае он должен просто вернуть void), выполнение продолжается в табличном представлении.

Делегаты - это концепция. Это не языковая функция Objective-C. Член delegate UITableView похож на любой другой объект. Хотя делегаты обычно не сохраняются, они вместо этого назначаются (чтобы избежать retain циклов).

Они очень удобны, когда вы разбираетесь в них. Я предлагаю попрактиковаться с такими примерами, как TableViews (NSTableView, как я уже говорил ранее, работает аналогичным образом, только с другими методами).

person jbrennan    schedule 28.08.2009
comment
Таким образом, это класс, который объект вызывает, когда что-то делается с самим собой. И в зависимости от того, что с собой сделали, он вызывает определенный метод из класса делегата. Правильный? Это правильный способ создания класса делегата или это просто еще один обычный класс с методами, которые могут вызывать другие классы? - person ; 29.08.2009
comment
Технически делегат - это объект. У вас может быть пятьдесят экземпляров объекта, реализующего некоторые методы делегата, но только тот экземпляр, который устанавливается в качестве делегата, имеет свои методы. Правильный способ создания делегата - определить протокол, который определяет набор методов, которые должны / могут быть реализованы. - person johnw188; 29.08.2009
comment
@Avizz: Вы правы, делегатом может быть любой объект, какой захотите. Хотя обычно это будет тот, который conforms to a protocol. Если объект соответствует данному протоколу, это в основном означает, что я обещаю, что реализовал все, что от меня требуется в данном протоколе ». Таким образом, когда объект отправляет сообщение делегату, они уверены, что делегат знает, что делать. - person jbrennan; 29.08.2009
comment
Еще одна вещь, чтобы убедиться, что я прав ... Когда, скажем, щелкают ячейку, он использует экземпляр объекта делегата и отправляет ему сообщение. У класса делегата есть метод для работы с тем, что он получил, и метод, основанный на полученной информации, что-то делает? Правильный? - person ; 29.08.2009
comment
@Avizz: Верно. В моем примере таблицы, когда ячейка выбрана, таблица затем подготавливает объект с именем indexPath (который говорит, какая строка была выбрана), а затем передает его делегату в вызове метода выше. Итак, delegate, который может быть таким же простым, как подкласс NSObject, реализует описанный выше метод. Когда таблица вызывает его с этим indexPath, наш делегат выполняет метод, используя indexPath, делает свое дело и возвращает void. Методы делегата не должны возвращать void, но этот метод возвращает. Некоторые возвращают bool, некоторые - float (например, высоту ячейки) и т. Д. - person jbrennan; 29.08.2009
comment
Источники данных не являются делегатами. - person bbum; 29.08.2009
comment
@bbum: Правильно, уточню. - person jbrennan; 29.08.2009

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

Вы используете наследование, когда хотите, чтобы связанные классы совместно использовали методы. Вы используете делегаты, когда хотите, чтобы несвязанные классы совместно использовали методы. Подход делегата позволяет объекту из одного класса наследовать методы из другого несвязанного класса. «Объект-делегат» или «объект-представитель» - это объект, наследуемый от внешнего класса; он обозначен как представитель, делегат этого удаленного класса, поэтому, когда вы назначаете объект в качестве делегата классу, вы даете ему разрешение также представлять этот класс, даже если он обычно не наследуется от этого класса . (Если он унаследован от класса, вам не нужно устанавливать его в качестве делегата этого класса; у него уже будет доступ к методам класса. Но вы хотите, чтобы этот несвязанный объект наследовал некоторые методы из класса, представляющие этот и возвращая некоторую информацию обратно своему классу-отправителю, поэтому вы делаете его представителем-делегатом этого класса, даже если он наследуется от другого набора классов.) Это, по сути, позволяет одному несвязанному классу наследовать от другого несвязанного класса, но с минимум осложнений по своей основной наследственной линии.

Вы используете систему делегатов, когда хотите, чтобы объект выполнял код из отдельного класса. Например, как описано здесь, когда вы нажимаете Enter в текстовом поле, текстовое поле действительно не знает, что делать. делать с этой информацией. Что он делает, так это смотрит на класс своего объекта-делегата (например, оконный контроллер документа или документ) и использует соответствующий метод этого класса, который соответствует соответствующему методу текстового поля, найденному в его протоколе делегата текстового поля, что-то вроде textFieldShouldReturn. Итак, в этом случае вы устанавливаете контроллер окна или документ в качестве делегата текстового поля, потому что текстовому полю нужен этот класс для представления информации, которую ему было дано.

Классу A нужен метод из класса B, но класс A не наследуется от класса B. Сначала сообщите компилятору, что класс A соответствует протоколу класса B:

@interface ClassA : NSObject <ClassBDelegateProtocol>

В документации Apple каждая ссылка на класс отображается вверху: «Наследуется от» и «Соответствует». Например, NSDocumentController наследуется от NSObject и соответствует NSUserInterfaceValidations, NSCoding и NSObject(NSObject). Соответствие NSCoding взято из объявления интерфейса в NSDocumentController.h

@interface NSDocumentController : NSObject <NSCoding>

и NSUserInterfaceValidations из объявления метода в NSDocumentController.h

-(BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item;

вместе с #import

#import <AppKit/NSUserInterfaceValidation.h>

Итак, в то время как NSDocumentController наследуется от NSObject, ему нужна помощь также от NSCoding и NSUserInterfaceValidation. И он получает помощь от этих внешних классов, соответствуя их методам протокола, определяя себя как соответствующее этим методам и импортируя все необходимые файлы заголовков для использования этих методов. .

person Sparky    schedule 04.10.2013

Взгляните на мое руководство по шаблону проектирования делегата здесь: http://www.jonmsterling.com/blog/?p=74. Надеюсь, это поможет.

person Jonathan Sterling    schedule 29.08.2009

Вы не одиноки, я все еще учусь этому сам. Думаю, будет лучше, если я попытаюсь объяснить это словами, а не кодом. Существует протокол, в котором содержится определенный метод или действие, которое вы хотите выполнить. Делегат берет метод из протокола, поэтому может использовать его для облегчения действия или метода. Я нашел эту статью очень полезной http://rypress.com/tutorials/objective-c/protocols. Надеюсь это поможет.

person Khalid Mohamed    schedule 23.10.2016