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.

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.


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.


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.