Приведите экземпляр класса к @protocol в Objective-C

102

У меня есть объект (UIViewController), который может соответствовать или не соответствовать определенному мною протоколу.

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

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)]) {
    [self.myViewController protocolMethod]; // <-- warning here
}

Однако XCode показывает предупреждение:

warning 'UIViewController' may not respond to '-protocolMethod'

Как правильно предотвратить это предупреждение? Я не могу бросить self.myViewControllerкак MyProtocolкласс.

Форд
источник

Ответы:

172

Правильный способ сделать это:

if ([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
        UIViewController <MyProtocol> *vc = (UIViewController <MyProtocol> *) self.myViewController;
        [vc protocolMethod];
}

Приведение UIViewController <MyProtocol> *типа переводится как «vc - это объект UIViewController, который соответствует MyProtocol», тогда как using id <MyProtocol>переводится как «vc - это объект неизвестного класса, который соответствует MyProtocol».

Таким образом, компилятор предоставит вам надлежащую проверку типа vc- компилятор выдаст предупреждение только в том случае, если вызывается какой-либо метод, который не объявлен ни для одного из них, UIViewControllerлибо <MyProtocol>. idследует использовать только в том случае, если вы не знаете класс / тип приводимого объекта.

Ник Фордж
источник
2
При использовании протоколов вам действительно не следует заботиться о типе объекта - весь смысл протокола в том, что любой тип объекта может принять его и использовать без необходимости преобразования к конкретному объекту. Таким образом, я бы рекомендовал использовать ответ на @andy везде вы произнесение к протоколу , а не выше - id<MyProtocol> p = (id<MyProtocol>)self.myViewController;Этот ответ и @andys оба правильно, но его это более правильно.
memmons
2
@Answerbot, ваш комментарий неверен и упускает из виду то, что я сделал в последнем абзаце моего ответа. Тип объекта может вас волновать, а может и нет, это зависит от ситуации. Что произойдет , если вы хотите отправить сообщение объявлено UIViewControllerв vcв примере в моем ответе, и это объявлено как id <MyProtocol>?
Ник Фордж
Не уверены, что в отношении моего комментария неверно? В любом случае, если вы проверяете, соответствует ли объект протоколу, зачем вам тогда вызывать какой-то другой метод, не связанный с протоколом? Я не могу припомнить, чтобы мне когда-либо приходилось делать это или видеть это в коде, который я просмотрел. Мне кажется, это запах кода.
memmons 01
То, что вы его не видели / не использовали, не означает, что это запах кода. Вот фрагмент кода, показывающий один пример того, где выброс информации о типе с помощью использования idявляется проблемой: gist.github.com/nsforge/7743616
Ник Фордж
60

Вы можете использовать это так:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
    id<MyProtocol> p = (id<MyProtocol>)self.myViewController;
    [p protocolMethod];
}

Это тоже меня немного подбросило. В Objective-C протокол - это не сам тип, поэтому вам нужно указать id(или какой-либо другой тип, например NSObject) вместе с протоколом, который вы хотите.

Энди
источник
Ах, здорово, спасибо. Я только что проверил и увидел, что кастинг (id)тоже работает. Это дурной тон?
Ford
1
Если вы приведете его как id <MyProtocol>, компилятор предупредит вас, если вы используете методы, которые не определены в этом протоколе.
dreamlax
1
@dreamlax - так компилятор выполняет проверку типов по протоколам. См. Developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… для получения дополнительной информации.
Энди,
1
@Ford - было бы лучше использовать протокол специально, так как таким образом компилятор может выполнить за вас некоторую проверку типов.
Энди
1
@ Энди, я не думаю, что вам нужен '*', поскольку 'id' уже является указателем. Итак: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protocolMethod]; Или просто: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford