Lanzar una instancia de una clase a un @protocol en Objective-C

102

Tengo un objeto (un UIViewController) que puede o no cumplir con un protocolo que he definido.

Sé que puedo determinar si el objeto se ajusta al protocolo, luego llamar de forma segura al método:

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

Sin embargo, XCode muestra una advertencia:

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

¿Cuál es la forma correcta de prevenir esta advertencia? Parece que no puedo elegir self.myViewControllercomo MyProtocolclase.

Vado
fuente

Respuestas:

171

La forma correcta de hacer esto es hacer:

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

El UIViewController <MyProtocol> *tipo de conversión se traduce como "vc es un objeto UIViewController que cumple con MyProtocol", mientras que el uso se id <MyProtocol>traduce como "vc es un objeto de una clase desconocida que se ajusta a MyProtocol".

De esta manera, el compilador le proporcionará la verificación de tipos adecuada vc; el compilador solo le dará una advertencia si se llama a algún método que no esté declarado en UIViewControllero <MyProtocol>. idsolo debe usarse en la situación si no conoce la clase / tipo del objeto que se está lanzando.

Nick Forge
fuente
2
Al usar protocolos, realmente no debería preocuparse por el tipo de objeto: el objetivo de un protocolo es que cualquier tipo de objeto puede adoptarlo y usarse sin tener que convertirlo en el objeto específico. Por lo tanto, recomendaría usar la respuesta de @andy en cualquier lugar donde esté transmitiendo a un protocolo en lugar del anterior: id<MyProtocol> p = (id<MyProtocol>)self.myViewController;esta respuesta y @andys son correctas, pero la suya es más correcta.
memmons
2
@Answerbot, su comentario es incorrecto y no comprende el punto que hice en el último párrafo de mi respuesta. Puede que le importe o no el tipo de objeto, depende de la situación. ¿Qué pasa si desea enviar un mensaje declarada el UIViewControllerque vcen el ejemplo de mi respuesta, y se declara como id <MyProtocol>?
Nick Forge
¿No estás seguro de qué es incorrecto con respecto a mi comentario? En cualquier caso, si está comprobando si un objeto se ajusta a un protocolo, ¿por qué llamaría a otro método no relacionado con el protocolo? No recuerdo haber tenido que hacer esto ni haberlo visto en el código que he revisado. Me parece un olor a código.
memmons
El hecho de que no lo haya visto / usado no significa que sea un olor a código. Aquí hay un fragmento de código que muestra un ejemplo de dónde deshacerse de la información de tipo mediante el uso ides un problema: gist.github.com/nsforge/7743616
Nick Forge
60

Puedes lanzarlo así:

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

Esto también me desconcertó un poco. En Objective-C, el protocolo no es el tipo en sí, por lo que debe especificar id(o algún otro tipo, como NSObject) junto con el protocolo que desea.

Andy
fuente
Ah, genial, gracias. Acabo de comprobar y vi que fundirlo también (id)funciona. ¿Es eso de mala forma?
Ford
1
Si lo lanza como id <MyProtocol>, el compilador le advertirá si usa métodos que no están definidos en ese protocolo.
dreamlax
1
@dreamlax: así es como el compilador verifica los tipos de protocolos. Consulte developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… para obtener más información.
Andy
1
@ Ford: sería mejor usar el protocolo específicamente, ya que de esa manera el compilador puede realizar alguna verificación de tipo por usted.
Andy
1
@Andy, no creo que necesites el '*' ya que 'id' ya es un puntero. Entonces: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protocolMethod]; O simplemente: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford