Obiettivo-C sinteticamente protocolli

In Objective-C, a protocollo è un gruppo di metodi che possono essere implementati da qualsiasi classe. I protocolli sono essenzialmente gli stessi delle interfacce in C # e hanno entrambi obiettivi simili. Possono essere usati come un tipo di pseudo-dati, che è utile per assicurarsi che un oggetto digitato in modo dinamico possa rispondere a un certo insieme di messaggi. E, poiché ogni classe può "adottare" un protocollo, può essere utilizzato per rappresentare un'API condivisa tra classi completamente estranee.

La documentazione ufficiale discute sia un metodo informale sia un metodo formale per la dichiarazione dei protocolli, ma i protocolli informali sono in realtà solo un uso unico delle categorie e non offrono quasi altrettanti benefici dei protocolli formali. Con questo in mente, questo capitolo si concentra esclusivamente su formale protocolli.


Creazione di un protocollo

In primo luogo, diamo un'occhiata a come dichiarare un protocollo formale. Crea un nuovo file in Xcode e seleziona l'icona del protocollo Objective-C sotto Mac OS X> Cocoa:

Icona Xcode per i file di protocollo

Come al solito, questo ti richiederà un nome. Il nostro protocollo conterrà metodi per calcolare le coordinate di un oggetto, quindi chiamiamolo CoordinateSupport:

Denominazione del protocollo

Clic Il prossimo e scegli il percorso predefinito per il file. Questo creerà un protocollo vuoto che sembra quasi esattamente come un'interfaccia:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @fine

Certo, invece del @interfaccia direttiva, utilizza @protocollo, seguito dal nome del protocollo. Il la sintassi ci consente di incorporare un altro protocollo in CoordinateSupport. In questo caso, lo stiamo dicendo CoordinateSupport include anche tutti i metodi dichiarati nel NSObject protocollo (da non confondere con il NSObject classe).

Successivamente, aggiungiamo alcuni metodi e proprietà al protocollo. Funziona allo stesso modo della dichiarazione di metodi e proprietà in un'interfaccia:

#importare  @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (doppio) magnitudine; @fine

Adottare un protocollo

Qualsiasi classe che adotta questo protocollo è garantita per sintetizzare il X, y, e z proprietà e implementare il arrayFromPosition e grandezza metodi. Mentre questo non dice Come saranno implementati, ti darà l'opportunità di definire un'API condivisa per un insieme arbitrario di classi.

Ad esempio, se vogliamo entrambi Nave e Persona per essere in grado di rispondere a queste proprietà e metodi, possiamo dire loro di adottare il protocollo ponendolo in parentesi angolate dopo la dichiarazione della superclasse. Inoltre, come per l'utilizzo di un'altra classe, è necessario importare il file di protocollo prima di utilizzarlo:

#importare  #import "CoordinateSupport.h" @interface Person: NSObject  @property (copy) NSString * nome; @property (strong) NSMutableSet * amici; - (vuoto) say Hello; - (vuoto) sayGoodbye; @fine

Ora, oltre alle proprietà e ai metodi definiti in questa interfaccia, il Persona la classe è garantita per rispondere all'API definita da CoordinateSupport. Xcode ti avviserà che il Persona l'implementazione è incompleta finché non si sintetizza X, y, e z, e attuare arrayFromPosition e grandezza:

Avviso di implementazione incompleto per Person

Allo stesso modo, una categoria può adottare un protocollo aggiungendolo dopo la categoria. Ad esempio, per dire al Persona classe di adottare il CoordinateSupport protocollo nel Relazioni categoria, useresti la seguente riga:

@interface Person (Relations) 

E se la tua classe ha bisogno di adottare più di un protocollo, puoi separarli con virgole:

@interface Person: NSObject 

Vantaggi dei protocolli

Senza protocolli avremmo due opzioni per garantire entrambi Nave e Persona implementato questa API condivisa:

  1. Ripetizione delle stesse proprietà e dei medesimi metodi in entrambe le interfacce.
  2. Definisci l'API in una superclasse astratta e definisci Nave e Persona come sottoclassi.

Nessuna di queste opzioni è particolarmente allettante: la prima è ridondante e soggetta all'errore umano e la seconda è gravemente limitante, soprattutto se già ereditano da diverse classi genitore. Dovrebbe essere chiaro che i protocolli sono molto più flessibili e riutilizzabili, poiché proteggono l'API dall'essere dipendente da una particolare classe.

Il fatto che qualunque la classe può facilmente adottare un protocollo che consente di definire relazioni orizzontali su una gerarchia di classi esistente:

Collegamento di classi non correlate utilizzando un protocollo

A causa della natura flessibile dei protocolli, i vari framework iOS ne fanno buon uso. Ad esempio, i controlli dell'interfaccia utente vengono spesso configurati utilizzando il modello di progettazione della delega, in cui un oggetto delegato è responsabile della reazione alle azioni dell'utente. Invece di incapsulare le responsabilità di un delegato in una classe astratta e costringere i delegati a sottoclassi, iOS definisce l'API necessaria per il delegato in un protocollo. In questo modo, è incredibilmente facile qualunque oggetto di agire come oggetto delegato. Esploreremo questo in modo molto più dettagliato nella seconda parte di questa serie, iOS in modo succinto.


Protocolli come pseudo-tipi

I protocolli possono essere usati come tipi di dati psuedo. Invece di accertarsi che una variabile sia un'istanza di una classe, l'utilizzo di un protocollo come strumento di verifica del tipo garantisce che la variabile sia sempre conforme a un'API arbitraria. Ad esempio, il seguente persona la variabile è garantita per implementare l'API di CoordinateSupport.

Persona  * person = [[Person alloc] init];

Tuttavia, l'applicazione dell'adozione del protocollo è spesso più utile se utilizzata con id tipo di dati. Ciò ti consente di assumere determinati metodi e proprietà mentre trascura completamente la classe dell'oggetto.

E, naturalmente, la stessa sintassi può essere utilizzata con un parametro di metodo. Il seguente frammento aggiunge un nuovo getDistanceFromObject: metodo all'API il cui parametro è necessario per conformarsi CoordinateSupport protocollo:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (doppio) magnitudine; - (double) getDistanceFromObject: (id )l'oggetto; @fine

Si noti che è interamente possibile utilizzare un protocollo nello stesso file definito.

Controllo di conformità dinamico

Oltre al controllo del tipo statico discusso nell'ultima sezione, è anche possibile utilizzare il comando conformsToProtocol: metodo definito dal NSObject protocollo per verificare dinamicamente se un oggetto è conforme a un protocollo o meno. Questo è utile per prevenire errori quando si lavora con oggetti dinamici (oggetti digitati come id).

Il seguente esempio presuppone il Persona la classe adotta il CoordinateSupport protocollo, mentre il Nave la classe no. Usa un oggetto tipizzato dinamicamente chiamato mysteryObject per memorizzare un'istanza di Persona,e poi usa conformsToProtocol: per verificare se ha il supporto delle coordinate. Se lo fa, è sicuro usare il X, y, e z proprietà, così come gli altri metodi dichiarati nel CoordinateSupport protocollo:

// main.m #import  #import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool id mysteryObject = [[Person alloc] init]; [mysteryObject setX: 10.0]; [mysteryObject setY: 0.0]; [mysteryObject setZ: 7.5]; // Decommenta la riga successiva per vedere la parte "altro" del condizionale. // mysteryObject = [[Ship alloc] init]; if ([mysteryObject conformsToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "Ok per assumere il supporto delle coordinate."); NSLog (@ "L'oggetto si trova in (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]);  else NSLog (@ "Errore: non è sicuro assumere il supporto delle coordinate."); NSLog (@ "Non ho idea di dove sia l'oggetto ...");  restituisce 0; 

Se si decommenta la riga che riassegna il mysteryObject a a Nave esempio, il conformsToProtocol: il metodo tornerà NO, e non sarai in grado di utilizzare in tutta sicurezza l'API definita da CoordinateSupport. Se non sei sicuro del tipo di oggetto che una variabile manterrà, questo tipo di controllo dinamico del protocollo è importante per evitare che il tuo programma si arresti in modo anomalo quando tenti di chiamare un metodo che non esiste.

Notare anche il nuovo @protocollo() direttiva. Funziona molto simile @selettore(), tranne che al posto di un nome di metodo, accetta un nome di protocollo. Restituisce a Protocollo oggetto, che può essere passato a conformsToProtocol:, tra gli altri metodi integrati. Il file di intestazione del protocollo fa non devono essere importati per @protocollo() lavorare.


Protocolli di dichiarazione in avanti

Se si finisce per lavorare con molti protocolli, si finisce per imbattersi in una situazione in cui due protocolli si affidano l'un l'altro. Questa relazione circolare pone un problema al compilatore, dal momento che non può importare con successo nessuno dei due senza l'altro. Ad esempio, diciamo che stavamo cercando di astrarre alcune funzionalità GPS in a GPSSupport protocollo, ma vogliono essere in grado di convertire tra le coordinate "normali" del nostro esistente CoordinateSupport e le coordinate utilizzate da GPSSupport. Il GPSSupport il protocollo è piuttosto semplice:

#importare  #import "CoordinateSupport.h" @protocol GPSSupport  - (Void) copyCoordinatesFromObject: (id )l'oggetto; @fine

Questo non pone alcun problema, cioè finché non abbiamo bisogno di fare riferimento al GPSSupport protocollo da CoordinateSupport.h:

#importare  #import "GPSSupport.h" @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (doppio) magnitudine; - (double) getDistanceFromObject: (id )l'oggetto; - (void) copyGPSCoordinatesFromObject: (id )l'oggetto; @fine

Ora il CoordinateSupport.h il file richiede il GPSSupport.h file per compilare correttamente e viceversa. È un problema di tipo pollo o uovo, e al compilatore non piacerà molto:

Errore del compilatore causato da riferimenti al protocollo circolare

Risolvere la relazione ricorsiva è semplice. Tutto quello che devi fare è inoltrare-dichiarare uno dei protocolli invece di provare a importarlo direttamente:

#importare  @protocol CoordinateSupport; @protocol GPSSupport  - (Void) copyCoordinatesFromObject: (id )l'oggetto; @fine

Tutti @protocol CoordinateSupport; dice questo CoordinateSupport è davvero un protocollo e il compilatore può presumere che esista senza importarlo. Nota il punto e virgola alla fine dell'istruzione. Questo potrebbe essere fatto in uno dei due protocolli; il punto è rimuovere il riferimento circolare. Il compilatore non si cura di come lo fai.


Sommario

I protocolli sono una caratteristica incredibilmente potente di Objective-C. Ti consentono di acquisire relazioni tra classi arbitrarie quando non è fattibile collegarle a una classe genitore comune. Utilizzeremo diversi protocolli integrati in iOS in modo succinto, poiché molte delle funzioni principali di un'app per iPhone o iPad sono definite come protocolli.

Il prossimo capitolo introduce eccezioni ed errori, due strumenti molto importanti per gestire i problemi che inevitabilmente si presentano durante la scrittura dei programmi Objective-C.

Questa lezione rappresenta un capitolo di Objective-C, un eBook gratuito del team di Syncfusion.