путаница с взглядами, делегатами и выходами Objective-c

Я пытаюсь научить себя Objective-C, но, исходя из опыта работы с Python / Java, это оказывается очень трудным. Я попытался опубликовать несколько моментов, которые меня запутали, но они были помечены как слишком расплывчатые, поэтому я разобью их на разделы.

Во-первых, каждый пример делегата и выхода, который я нашел, использует код представления какао, чтобы донести идею. К сожалению, я еще недостаточно разбираюсь в коде, чтобы понять суть примера. Итак, может ли кто-нибудь привести более простой пример делегата? Насколько я понимаю, это способ подкласса; чем это лучше традиционного подкласса? Почему каждый проект какао автоматически включает appDelegate.m? Можно ли использовать делегаты для других целей, а не только для графического интерфейса?

Ладно, я понимаю. Итак, делегат - это класс, который соответствует протоколу какого-то другого класса. Протокол - это просто список методов и переменных, которые должны (или могут, если задано значение optional) быть реализованы в классе делегата. Чтобы создать делегата, вы должны использовать ключевое слово @interface, затем имя вашего делегата, а затем (в знаках ‹>) имя протокола? Итак, если класс c1 хочет настроить себя как делегат класса c, класс c должен сначала указать протокол, верно? Тогда вы сможете реализовать все в протоколе c в c1: @interface c1; Я чувствую, что мне здесь не хватает кое-чего, но, надеюсь, у меня правильные концепции. :) Это также объясняет загадочные знаки «меньше и больше»; они объявляют, какой интерфейс реализует делегат.

Выходы точно так же всегда привязаны к просмотру кода. Они кажутся своего рода системой обмена сообщениями между объектами. Это правильно? Опять же, пример простой розетки, не смешанной со сложными операторами графического интерфейса, был бы отличным. Значит, розетки никогда не нужны? Я знаю, что IBOutlet и IBAction не нужны, кроме использования с Interface Builder, но я думал, что выходы более общие, чем это? Документы, казалось, указывали, что они даже не предназначены специально для интерфейсов, но могут использоваться для чего угодно.

Заранее благодарю за любую помощь.


person AH16    schedule 22.01.2013    source источник
comment
Я обновил свой ответ в ответ на ваши дополнительные вопросы.   -  person Itai Ferber    schedule 22.01.2013


Ответы (5)


Обновление: делегаты не обязаны соответствовать протоколам. Протоколы просто упрощают требование, чтобы у некоторых классов были методы. Он позволяет вам наверняка знать, что объект, установленный в качестве делегата, реализует определенный метод, чтобы вы могли его безопасно вызывать, и позволяет компилятору проверить, действительно ли реализован этот метод (если вы объявляете переменную экземпляра делегата как id<SomeProtocol> delegate, компилятор выдаст предупреждение или ошибку, если вы попытаетесь установить delegate для объекта класса, который не соответствует SomeProtocol.

Протоколы помогают обеспечить безопасность, но они не являются строго необходимыми. У класса может быть делегат (или несколько!), И они вообще не обязаны соответствовать каким-либо протоколам.

Что касается выходов, нет, они используются специально и только с Interface Builder. Ключевые слова IBOutlet и IBAction не влияют на код (они даже удаляются перед компиляцией) - они всего лишь маркеры, которые должен искать Interface Builder, чтобы он знал, какие свойства и методы должны быть доступны в интерфейсе. Термин «выход» - это прямая ссылка на что-то, помеченное как IBOutlet, и на самом деле не используется в каком-либо другом контексте, который я могу сказать.

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


О делегатах: первое, что нужно понять, и это помогло мне на время, пока я не услышал правильное «Ага!» Момент заключается в том, что в «делегате» нет ничего особенного. Слово «делегат» - это просто название типа объекта, от которого зависит другой класс, очень часто для содержания или принятия решений. Разработчик будет использовать делегата, когда он не хочет (или не может) связывать один из своих классов с другим классом по имени - это объектно-ориентированный способ разделения и создания классов более универсальными.

Теперь очень часто классы требуют, чтобы у делегатов были определенные методы, на которые они полагаются, и один из способов гарантировать это - с помощью протокола (более известного как интерфейс в Java). Протоколы определяют список методов; классы «соответствуют» протоколу, если они декларируют это в своем интерфейсе (например, @interface IFObject : NSObject <SomeProtocol> { ... }) и если они реализуют все методы, которые им необходимы. Протоколы также могут иметь дополнительные методы.

Эта модель часто используется с контроллерами представлений, представлениями и графическим интерфейсом пользователя в целом, потому что многие классы AppKit и UIKit написаны как можно более общими. NSTableView, например, реализует самое базовое поведение, которое он может реализовать, не требуя какой-либо информации, зависящей от реализации; в остальном он полагается на другие объекты, соответствующие протоколам NSTableViewDelegate и NSTableViewDataSource. Любой объект может соответствовать протоколам, если они реализуют правильные методы (и в этом случае класс контроллера обычно реализует методы из обоих протоколов, но это не обязательно так) . Фактически, один из простых способов лучше понять эту тему - взглянуть на NSTableView - у него есть свойство delegate и свойство dataSource, но, по сути, они ничем не отличаются. delegate можно было бы назвать monkeyButt, и концепция все равно работала бы. Главное - не относиться к делегатам как к черному ящику - в них нет ничего особенного.

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


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

Как правило, они представляют собой более простой способ настройки интерфейса - они не являются строго необходимыми (вы можете создать интерфейс программно, без использования файла XIB), но если вы решите пойти по маршруту XIB, они помогут вам свяжите ваш интерфейс с вашим кодом.

person Itai Ferber    schedule 22.01.2013
comment
Хорошо ... вау ... Итак, делегат - это подкласс, но наоборот: там, где подкласс автоматически наследует все от своего суперпользователя, делегат реализует только то, для чего он предназначен. Интерфейс может быть помещен в класс, и любой делегат этого класса, который заявляет, что он соответствует этому интерфейсу, должен объявить все методы и свойства в этом интерфейсе. Однако делегат может быть делегатом класса, но не реализовывать интерфейс, если он не претендует на реализацию интерфейса. Это правильно? (Пожалуйста, скажите, что это так!) Спасибо за вашу помощь. - person AH16; 23.01.2013
comment
Да, это правильно. Только будьте осторожны со своей терминологией. Интерфейс, о котором вы говорите, называется протоколом в Objective-C (это интерфейс в смысле Java); Интерфейс Objective-C - это объявление класса. - person Itai Ferber; 23.01.2013
comment
В дополнение к одному пункту: протокол устанавливает ожидания относительно того, какие методы будут реализованы. Требование, чтобы делегат соответствовал протоколу, предполагает, что он будет реализовывать методы, которые требуются классу-владельцу; делегаты бесполезны без вызова методов, поэтому они всегда будут соответствовать конкретным протоколам, которые нужны объектам-владельцам. - person Itai Ferber; 23.01.2013
comment
Хорошо, хорошее замечание, к этому нужно немного привыкнуть. :) Итак, теперь, когда у меня есть теория, я собираюсь пересмотреть примеры кодирования, чтобы попытаться определить синтаксис. Большое спасибо за все ваши объяснения! - person AH16; 23.01.2013
comment
@ user1999060 Имейте в виду, что то, должен ли делегат соответствовать протоколу / интерфейсу, определяется самим объектом с делегатом. Если он объявляет свое свойство делегата как нормальное id, он может выбрать, соответствовать ему или нет. Если он объявляет это как id<ProtocolName>, то он должен соответствовать протоколу. Также имейте в виду, что даже если он действительно соответствует протоколу, нет необходимости всегда реализовывать все методы. Любые методы, объявленные под @optional, могут или не могут быть определены в делегате при вашем отступлении. - person Metabble; 23.01.2013

Делегат - это объект, которому делегирована некоторая задача объектом, делегатом которого он объявляет себя. Допустим, объект выполняет некоторую задачу, а затем ему необходимо уведомить объект, который «владеет» им, так сказать. Чтобы объект мог работать при любых условиях, ему нельзя разрешить знать, с каким типом объекта он должен контактировать, поэтому мы устанавливаем его делегата и придерживаемся его протокола. Установка делегата объекта - это как сказать: «Вот объект, который я хочу, чтобы вы использовали для связи с сообщениями, объявленными в вашем протоколе. Я обещаю, что объект действительно реализует методы в протоколе». Например, вы могли это увидеть.

@protocol SpriteDelegateProtocol
    @required
    - (void) projectionMatrix;
    @optional
    - (void) animation:(int)animationIndex willCompleteFrameNumber:(int)frame forSprite:(id)sender;
@end

@interface Sprite
@property (nonatomic, assign) id<SpriteDelegateProtocol> delegate;
@end

Интерфейс для нашего объекта-спрайта объявляет, что у него есть свойство, называемое делегатом, которое имеет тип id. Это может быть любой объект, но он должен соответствовать протоколу в треугольных скобках. Если он говорит, что придерживается протокола, он должен реализовать методы, объявленные в @required, и может реализовать методы, перечисленные в @optional. Нам решать, действительно ли наш делегат реализует необязательные методы, используя что-то вроде respondsToSelector:. Мы могли бы даже сохранить возвращаемое значение, если бы у нас было много методов для этого.

Делегат приложения является особенным в том смысле, что он является делегатом нашего объекта UIApplication. Он получает сообщения о состоянии приложения, например applicationDidEnterBackground:. Вы можете увидеть, какие методы используются в протоколе нашего делегата приложения здесь.

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

Выход - это способ подключения представления, созданного в NIB или раскадровке, к свойству в его контроллере. Итак, если вы поместите UIViewController в свою основную раскадровку и измените его класс на MyGreatControllerSubclass, а затем переместите кнопку в представление этого контроллера, вы можете подключить эту кнопку к «розетке» (свойству) в интерфейсе контроллера, чтобы вы сможете получить доступ к кнопке с контроллера позже.

person Metabble    schedule 22.01.2013

Отсутствие делегатов - это не способ создания подклассов. Я тоже пришел с Java и занимаюсь O-C уже почти 5 лет.

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

Лучше всего думать о делегатах как о примерах обратных вызовов, ориентированных на сообщения / события. Слышали ли вы когда-нибудь поговорку о том, что фреймворк отличается от обычного программирования тем, что он говорит вам, что он хочет от вас сделать для обеспечения желаемых результатов? Вот что такое делегирование. Вчера вечером я делал просмотр коллекции и не мог вызвать меню редактирования. Просто предоставьте делегирование трех методов, ничего не знайте о том, как вызывается меню, где находится обработчик жестов длительного нажатия и т. Д.

Обратите внимание, что делегаты - это клей, который делает внедрение зависимостей ненужным в мире O-C. Я предпочитаю их.

person Rob    schedule 22.01.2013

Делегаты - это общий шаблон проектирования (http://en.wikipedia.org/wiki/Delegation_pattern), они не зависят от цели.

Выходы делают возможной привязку Контроллера и Представления (GUI). Может быть, вы знакомы с парадигмой MVC (модель-представление-контроллер)? Таким образом, вы создаете свой графический интерфейс (представление из MVC) с помощью Interface Builder и привязываете элементы этого графического интерфейса, такие как кнопки, текстовые поля и т. Д., К вашему коду Obj-C (контроллер из MVC) с помощью выходов.

person Leo Chapiro    schedule 22.01.2013

Взгляните на этот пост: Соответствие цели- Протоколы C в кодах C #

Не беспокойтесь о слове «C #» в заголовке, вы можете просто проигнорировать эту часть. Основная часть этого поста рассказывает о protocols и delegates в Objective-C. Это может быть полезно.

person Community    schedule 31.01.2013