Trasmetti un'istanza di una classe a un @protocol in Objective-C


102

Ho un oggetto (un UIViewController) che può o non può essere conforme a un protocollo che ho definito.

So di poter determinare se l'oggetto è conforme al protocollo, quindi chiamare in sicurezza il metodo:

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

Tuttavia, XCode mostra un avviso:

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

Qual è il modo giusto per evitare questo avviso? Non riesco a lanciare self.myViewControllercome MyProtocolclasse.

Risposte:


171

Il modo corretto per farlo è fare:

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

Il UIViewController <MyProtocol> *type-cast si traduce in "vc è un oggetto UIViewController conforme a MyProtocol", mentre using si id <MyProtocol>traduce in "vc è un oggetto di una classe sconosciuta conforme a MyProtocol".

In questo modo il compilatore ti darà il controllo del tipo corretto vc- il compilatore ti darà un avvertimento solo se un metodo che non è dichiarato su nessuno dei due UIViewControllero <MyProtocol>viene chiamato. iddovrebbe essere utilizzato solo nella situazione se non si conosce la classe / il tipo dell'oggetto che si sta eseguendo.


2
Quando si utilizzano i protocolli, non si dovrebbe davvero preoccuparsi del tipo di oggetto: il punto centrale di un protocollo è che qualsiasi tipo di oggetto può adottarlo ed essere utilizzato senza dover eseguire il cast sull'oggetto specifico. Quindi, consiglierei di usare la risposta di @andy ovunque tu stia trasmettendo a un protocollo invece di quanto sopra - id<MyProtocol> p = (id<MyProtocol>)self.myViewController;Questa risposta e @andys sono entrambe corrette, ma la sua è più corretta.
memmons

2
@Answerbot il tuo commento non è corretto e manca il punto che ho detto nell'ultimo paragrafo della mia risposta. Potrebbe interessarti o meno il tipo di oggetto, dipende dalla situazione. Che cosa succede se si desidera inviare un messaggio ha dichiarato il UIViewControllerper vcl'esempio nella mia risposta, ed è dichiarata come id <MyProtocol>?
Nick Forge

Non sei sicuro di cosa sia sbagliato riguardo al mio commento? In ogni caso, se stai controllando se un oggetto è conforme a un protocollo, perché dovresti chiamare un altro metodo non correlato al protocollo? Non ricordo di aver mai avuto bisogno di farlo o di averlo visto nel codice che ho esaminato. Mi sembra un odore di codice.
memmons

Solo perché non l'hai visto / usato, non significa che sia un odore di codice. Ecco uno snippet di codice che mostra un esempio di dove eliminare le informazioni sul tipo utilizzando idè un problema: gist.github.com/nsforge/7743616
Nick Forge

60

Puoi trasmetterlo in questo modo:

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

Anche questo mi ha sconvolto per un po '. In Objective-C, il protocollo non è il tipo stesso, quindi è necessario specificare id(o qualche altro tipo, ad esempio NSObject) insieme al protocollo che si desidera.


Ah, bene, grazie. Ho appena controllato e ho visto che anche il casting (id)funziona. È una cattiva forma?
Ford

1
Se lo esegui come id <MyProtocol>, il compilatore ti avviserà se utilizzi metodi che non sono definiti in quel protocollo.
dreamlax

1
@dreamlax - Questo è il modo in cui il compilatore esegue il controllo del tipo rispetto ai protocolli. Vedi developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… per maggiori informazioni.
Andy

1
@ Ford - sarebbe meglio usare il protocollo in modo specifico, poiché in questo modo il compilatore può eseguire un controllo del tipo per te.
Andy

1
@ Anddy, non penso che tu abbia bisogno del '*' poiché 'id' è già un puntatore. Quindi: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protocolMethod]; O semplicemente: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.