Какой смысл необязательного метода в протоколе? - PullRequest
4 голосов
/ 29 января 2011

Если мне все равно придется вызывать "responsedsToSelector:" для объекта, что на самом деле делает для меня определение метода как необязательного?

Например, скажем, у меня есть некоторый код, подобный этому

id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod)]){
  [myObject aMethod];
}

Пока MyClass реализует aMethod, этот код не будет работать точно так же, или MyProtocolопределяет "aMethod"?

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

Ответы [ 7 ]

8 голосов
/ 29 января 2011

Практически то, что написано на банке: это относится к дополнительным функциям.Если ваш aMethod содержит необходимые функциональные возможности для работы вашего приложения, он должен быть @required.В противном случае, если он предоставляет разработчику дополнительные функции для выполнения других задач, но его отсутствие не окажет негативного влияния на то, как он должен работать (т.е. все в порядке, что разработчик не отвечает на селектор @selector(aMethod)), вы можете сделать это@optional.

Вы видите много в протоколах делегатов.Например, UITableViewDelegate для iOS: вот набор методов протокола делегата, которые определяют представления для верхних и нижних колонтитулов представления таблицы:

  • tableView:viewForHeaderInSection:
  • tableView:viewForFooterInSection:
  • tableView:heightForHeaderInSection:
  • tableView:heightForFooterInSection:

Если они не реализованы делегатом, UIKit просто рисует верхние и нижние колонтитулы раздела по умолчанию для заданного tableView, которые поставляются предварительно упакованными с элементом UITableView по умолчанию.Однако, если они реализованы, UIKit использует настраиваемые представления верхнего и нижнего колонтитула раздела, которые предоставляются этими методами вместе с tableView.

. Ключевое слово @optional в значительной степени говорит person написание класса для реализации делегата, что эти методы являются необязательными.Я считаю, что UIKit в любом случае выполняет conformsToProtocol: и respondsToSelector: внутренние проверки.

1 голос
/ 30 января 2011

@optional / @required не только для людей - хотя это само по себе является хорошей причиной! - это также для машин. Компилятор, статический анализатор и т. Д. Могут использовать их, чтобы определить, предоставил ли класс, реализующий @protocol, все необходимые методы, и определить, какие дополнительные методы предоставляются.

0 голосов
/ 30 января 2013

Я думаю, что большая разница в способе вызова метода!

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

id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod:)]){
  NSArray *args = [NSArray arrayWithObjects:@"First arg",[NSNumber numberWithInt:2],[NSNumber numberWithBool:YES],nil];
  [myObject performSelector:(@selector(aMethod:) withObject:args];
}

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

id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethodWithFirstArg:second:third:)]){
  [myObject aMethodWithFirstArg:@"First arg" second:2 third:YES];
}

Тот же результат, но я предпочитаю второй с протоколом!

0 голосов
/ 03 августа 2012

Протоколы предназначены только для документации и проверки времени компиляции. После запуска программы среда выполнения не знает и не заботится о том, какие статические типы имеют ваши объекты (за исключением случая, когда метод возвращает структуру, как указано в newacct).

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

Учитывая, что у вас есть протоколы, как только вы объявляете объект соответствующим протоколу, например

id<MyProtocol> foo;

Это немедленно включает проверку метода обратно. Без дополнительных методов это означает, что

if ([foo respondsToSelector: @selector(myOptionalSelector)])
{
    [foo myOptionalSelector];
}

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

0 голосов
/ 03 августа 2012

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

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

0 голосов
/ 25 марта 2011

Также см. Apple Общение с объектами , в которой рассматриваются делегаты, протоколы и селекторы. Несмотря на то, что он указан в Mac OS X, большинство (если не все), похоже, применимо и к iOS.

0 голосов
/ 30 января 2011

Morgancodes,

Вам НЕ нужно вызывать respondsToSelector для каждого метода, объявленного в протоколе, если вы также используете протоколы для проверки типов. Простое объяснение можно найти Здесь

Frank

...