Lançar uma instância de uma classe para um @protocol em Objective-C

102

Eu tenho um objeto (um UIViewController) que pode ou não estar em conformidade com um protocolo que defini.

Sei que posso determinar se o objeto está em conformidade com o protocolo e, em seguida, chamar o método com segurança:

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

No entanto, o XCode mostra um aviso:

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

Qual é a maneira certa de evitar esse aviso? Não consigo lançar self.myViewControllercomo uma MyProtocolclasse.

Ford
fonte

Respostas:

171

A maneira correta de fazer isso é:

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

O UIViewController <MyProtocol> *tipo-cast se traduz em "vc é um objeto UIViewController que está em conformidade com MyProtocol", enquanto usandoid <MyProtocol> traduz em "vc é um objeto de uma classe desconhecida que está em conformidade com MyProtocol".

Desta forma, o compilador fornecerá a verificação de tipo apropriada vc- o compilador apenas fornecerá um aviso se algum método não declarado UIViewControllerou <MyProtocol>for chamado. idsó deve ser usado na situação se você não souber a classe / tipo do objeto que está sendo lançado.

Nick Forge
fonte
2
Ao usar protocolos, você realmente não deve se preocupar com o tipo de objeto - o ponto principal de um protocolo é que qualquer tipo de objeto pode adotá-lo e ser usado sem ter que lançar para o objeto específico. Portanto, eu recomendaria usar a resposta @andy em qualquer lugar em que você esteja convertendo para um protocolo em vez da id<MyProtocol> p = (id<MyProtocol>)self.myViewController;resposta acima - esta resposta e @andy estão corretas, mas a dele é mais correta.
memmons
2
@Answerbot seu comentário está incorreto e não entendeu o que eu disse no último parágrafo da minha resposta. Você pode ou não se preocupar com o tipo de objeto, depende da situação. O que acontece se você quiser enviar uma mensagem declarada UIViewControllercomo vcno exemplo em minha resposta e ela for declarada como id <MyProtocol>?
Nick Forge
Não tem certeza do que está incorreto em relação ao meu comentário? Em qualquer caso, se você está verificando se um objeto está em conformidade com um protocolo, por que então chamaria algum outro método não relacionado ao protocolo? Não me lembro de precisar fazer isso ou de ter visto isso no código que analisei. Parece um cheiro de código para mim.
memmons 01 de
Só porque você não viu / usou, não significa que é um cheiro de código. Aqui está um trecho de código mostrando um exemplo de onde jogar fora informações de tipo usando idé um problema: gist.github.com/nsforge/7743616
Nick Forge
60

Você pode lançá-lo assim:

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

Isso me abalou um pouco também. Em Objective-C, o protocolo não é o tipo em si, então você precisa especificar id(ou algum outro tipo, como NSObject) junto com o protocolo que você deseja.

Andy
fonte
Ah, legal, obrigado. Acabei de verificar e vi que o lançamento também (id)funciona. Isso é má forma?
Ford
1
Se você convertê-lo como id <MyProtocol>, o compilador irá avisá-lo se você usar métodos que não estão definidos nesse protocolo.
dreamlax
1
@dreamlax - É assim que o compilador verifica os tipos de protocolos. Consulte developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… para obter mais informações.
Andy
1
@Ford - seria melhor usar o protocolo especificamente, já que dessa forma o compilador pode realizar alguma verificação de tipo para você.
Andy
1
@Andy, não acho que você precise do '*', pois 'id' já é um ponteiro. Portanto: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protocolMethod]; Ou apenas: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford,