Подтвердить что ты не робот

Цель-C: блоки против селекторов против протоколов

Я часто нахожу, что я пишу классы "утилиты", которые можно повторно использовать во всех моих проектах.

Например, предположим, что у меня есть представление "Адресная книга". Возможно, мне захочется использовать мою адресную книгу, чтобы выбрать, кому отправлено электронное письмо, или, может быть, кто добавится к запросу собрания.

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

Похоже, что в этом сценарии есть четыре (разумных) подхода;

  • Создайте протокол AddressBookDelegate и соответствующее свойство делегата в AddressBookController. Затем используйте сообщения, определенные в протоколе, чтобы сообщить результат (аналогично UIActionSheetDelegate).

  • Создайте "неофициальный" протокол "AddressBookDelegate" и соответствующее свойство делегата в AddressBookController, но тип свойства делегата будет "id" и будет проверяться во время выполнения с помощью "responsesToSelector:", чтобы узнать, делегат реализует методы, которые нам нужны (кажется, что большинство фреймворков началось таким образом).

  • Передайте AddressBookController идентификатор, представляющий делегат, а также два SEL, которые определяют методы вызова, когда пользователь выбирает пользователя или отменяет запрос. Преимущество, которое я вижу в этом; предположим, что один контроллер поддерживает BOTH, отправляя электронные письма и настраивая встречи (в этом примере я знаю, что это плохой дизайн... но можно представить более общую ситуацию, когда это выглядит вполне разумным для класса утилит). В этом случае вы могли бы передайте адресату AddressBookController разные SEL в зависимости от того, добавляете ли вы пользователей к электронной почте или добавляете пользователей на встречу... огромное улучшение по сравнению с iVar, чтобы указать состояние контроллера.

  • Передайте AddressBookController два блока; один для запуска, когда пользователь выбирает кого-либо из адресной книги, а другой для запуска, если пользователь отменяет запрос.

Блоки были настолько полезны для меня, и SO намного более изящный, я почти смутился, когда НЕ использовал их.

Я надеюсь, что более опытные члены сообщества StackOverflow, чем я могу помочь с их мыслями по этой теме.

4b9b3361

Ответ 1

"Традиционный" способ сделать это - это протокол. Неформальные были использованы до того, как @protocol был добавлен к языку, но это было до моего времени и, по крайней мере, в течение последних нескольких лет неофициальные протоколы были обескуражены, особенно с учетом спецификатора @optional. Что касается "делегата", который передает два SELs, это просто кажется более уродливым, чем объявление формального протокола, и, как правило, мне не кажется правильным. Блоки очень новые (особенно на iOS), так как все это происходит, и пока нам еще предстоит увидеть огромный объем документации/блогов в лучшем проверенном стиле, мне нравится идея, и это похоже на одно из блоки, которые лучше всего подходят для: аккуратных новых структур управления потоком.

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

[Controller askForSelection:^(id selection){
  //blah blah blah
} canceled:^{
  //blah blah blah
}];

вероятно, намного сложнее, чем определение двух дополнительных методов и протокола для них (формально или иначе) или передачи SEL и хранения их в ivars и т.д.

Ответ 2

Я бы просто пошел с вашим первым подходом. Это испытанный образец в Cocoa и, похоже, очень хорошо вписывается в то, что вы делаете.

Несколько комментариев по другим подходам:

  • Неофициальный протокол. На самом деле я не вижу никакого преимущества в этом отношении по формальному протоколу. С тех пор, как формальные протоколы получили методы @optional, полезность неофициальных протоколов намного меньше.
  • Передача SELs. Я не думаю, что это установленный шаблон в Cocoa. Я лично не считаю это лучше, чем подход делегата, но если он лучше подходит вашему мышлению, тогда идите. Вы действительно не избавляетесь от состояния; вы просто трансформируетесь во что-то другое. Лично я предпочел бы иметь ivar, который я могу установить и проверить, не используя типы селекторов.
  • Передача блоков. Это своего рода подход нового возраста, и он имеет некоторые достоинства. Я думаю, вам нужно быть осторожным, хотя, на мой взгляд, он не очень хорошо масштабируется. Например, если бы все NSTableView делегировать и методы источника данных были все блоки, я лично считаю, что это несколько раздражает. Представьте, что если вы хотите установить 10 различных блоков, ваш метод -awakeFromNib (или любой другой) будет довольно большим. В этом случае индивидуальные методы кажутся более подходящими. Однако, если вы уверены, что никогда не собираетесь идти дальше, скажем, двумя способами, тогда блок-подход представляется более разумным.