În obiectivul C, a protocol este un grup de metode care pot fi implementate de orice clasă. Protocoalele sunt în esență aceleași ca și interfețele din C #, și ambele au scopuri similare. Acestea pot fi folosite ca tip de pseudo-date, care sunt utile pentru a vă asigura că un obiect cu text dinamic poate răspunde la un anumit set de mesaje. Și, deoarece orice clasă poate "adopta" un protocol, acestea pot fi folosite pentru a reprezenta un API comun între clase complet neînrudite.
Documentația oficială discută o metodă informală și formală de declarare a protocoalelor, însă protocoalele informale sunt într-adevăr doar o utilizare unică a categoriilor și nu oferă aproape toate avantajele ca protocoalele formale. Având în vedere acest lucru, acest capitol se concentrează numai pe formal protocoale.
În primul rând, să aruncăm o privire asupra modului de declarare a unui protocol formal. Creați un fișier nou în Xcode și selectați pictograma protocolului Obiectiv-C de sub Mac OS X> Cacao:
Pictograma Xcode pentru fișierele de protocolCa de obicei, aceasta vă va solicita un nume. Protocolul nostru va conține metode pentru calcularea coordonatelor unui obiect, așa că o vom numi CoordinateSupport:
Denumirea protocoluluiClic Următor → și alegeți locația implicită pentru fișier. Acest lucru va crea un protocol gol care arată aproape exact ca o interfață:
// CoordinateSupport.h #import@protocol CoordinateSupport @Sfârșit
Desigur, în loc de @interface
directivă, pe care o folosește @protocol
, urmată de numele protocolului.
sintaxa ne permite să includem un alt protocol în CoordinateSupport
. În acest caz, spunem asta CoordinateSupport
include, de asemenea, toate metodele declarate în NSObject
protocol (nu trebuie confundat cu NSObject
clasă).
Apoi, să adăugăm câteva metode și proprietăți protocolului. Aceasta funcționează în același mod ca și declanșarea metodelor și a proprietăților într-o interfață:
#import@protocol CoordinateSupport @property dublu x; @property dublu y; @property double z; - (NSArray *) arrayFromPoziție; - magnitudinea (dublă); @Sfârșit
Orice clasă care adoptă acest protocol este gata să sintetizeze X
, y
, și z
proprietăți și să pună în aplicare arrayFromPosition
și mărime
metode. În timp ce acest lucru nu spune Cum ele vor fi implementate, vă oferă posibilitatea de a defini un API comun pentru un set arbitrar de clase.
De exemplu, dacă vrem ambele Navă
și Persoană
pentru a putea răspunde acestor proprietăți și metode, le putem spune să adopte protocolul introducându-l în paranteze înclinate după declarația superclass. De asemenea, rețineți că, la fel ca utilizarea unei alte clase, trebuie să importați fișierul de protocol înainte de al utiliza:
#import#import "CoordinateSupport.h" @interface Persoană: NSObject @property (copy) NSString * nume; @ prietenii (puternic) NSMutableSet *; - (void) sayHello; - (void) sayGoodbye; @Sfârșit
Acum, în plus față de proprietățile și metodele definite în această interfață, Persoană
clasa este garantată să răspundă la API definit de CoordinateSupport
. Xcode vă va avertiza că Persoană
implementarea este incompletă până când sintetizați X
, y
, și z
, și să pună în aplicare arrayFromPosition
și mărime
:
De asemenea, o categorie poate adopta un protocol prin adăugarea după categorie. De exemplu, pentru a spune Persoană
clasa de a adopta CoordinateSupport
protocol în Relaţii
categorie, ați folosi următoarea linie:
@ persoană interfață (relații)
Și dacă clasa dvs. trebuie să adopte mai mult de un protocol, le puteți separa prin virgule:
@ interfață persoană: NSObject
Fără protocoale, am avea două opțiuni pentru a ne asigura atât Navă
și Persoană
implementat acest API comun:
Navă
și Persoană
ca subclase.Nici una dintre aceste opțiuni nu este deosebit de atrăgătoare: primul este redundant și predispus la erori umane, iar al doilea este extrem de limitator, mai ales dacă deja moștenesc din diferite clase de părinți. Ar trebui să fie clar că protocoalele sunt mult mai flexibile și reutilizabile, deoarece protejează API-ul de a fi dependent de o anumită clasă.
Faptul că orice clasa poate adopta cu ușurință un protocol care face posibilă definirea relațiilor orizontale deasupra unei ierarhii de clase existente:
Conectarea clasei care nu au legătură cu un protocolDatorită naturii flexibile a protocoalelor, diferitele cadre iOS le folosesc bine. De exemplu, comenzile interfeței cu utilizatorul sunt adesea configurate folosind modelul de design al delegării, în care un obiect delegat este responsabil pentru reacția la acțiunile utilizatorului. În loc să încapsuleze responsabilitățile unui delegat într-o clasă abstractă și să-i forțeze pe delegați să-l subclass, iOS definește API-ul necesar pentru delegat într-un protocol. În acest fel, este incredibil de ușor de făcut orice obiect de a acționa ca obiect delegat. Vom explora acest lucru cu mult mai multe detalii în a doua jumătate a acestei serii, iOS Succinct.
Protocoalele pot fi utilizate ca tipuri de date psuedo-date. În loc să se asigure că o variabilă este o instanță a unei clase, folosirea unui protocol ca instrument de verificare a tipului asigură că variabila este întotdeauna conformă cu un API arbitrar. De exemplu, următoarele persoană
este garantată implementarea API-ului CoordinateSupport.
Persoană* person = = [Person alocare] init];
Totuși, adoptarea adoptării protocolului este adesea mai utilă atunci când este utilizată cu id
tip de date. Aceasta vă permite să vă asumați anumite metode și proprietăți, ignorând complet clasa obiectului.
Și, desigur, aceeași sintaxă poate fi utilizată cu un parametru de metodă. Fragmentul următor adaugă un nou fragment getDistanceFromObject:
la API a cărui parametru este necesar să se conformeze CoordinateSupport
protocol:
// CoordinateSupport.h #import@protocol CoordinateSupport @property dublu x; @property dublu y; @property double z; - (NSArray *) arrayFromPoziție; - magnitudinea (dublă); - (dublu) getDistanceFromObject: (id )obiectul; @Sfârșit
Rețineți că este cu totul posibil să utilizați un protocol în același fișier așa cum este definit.
În plus față de verificarea tipului static discutată în ultima secțiune, puteți utiliza și conformsToProtocol:
metoda definită de NSObject
protocol pentru a verifica dinamic dacă un obiect este conform cu un protocol sau nu. Acest lucru este util pentru prevenirea erorilor atunci când lucrați cu obiecte dinamice (obiecte tastate ca id
).
Următorul exemplu presupune Persoană
clasa adoptă CoordinateSupport
protocol, în timp ce Navă
clasa nu. Utilizează un obiect tastat dinamic numit mysteryObject
pentru a stoca o instanță de Persoană
,și apoi utilizează conformsToProtocol:
pentru a verifica dacă are suport de coordonare. În caz contrar, este sigur să utilizați X
, y
, și z
proprietăți, precum și celelalte metode declarate în CoordinateSupport
protocol:
// main.m #import#import "Person.h" #import "Ship.h" int principal (int argc, const char * argv []) @autoreleasepool id mysteryObject = [[Person aloca] init]; [mysteryObject setX: 10.0]; [mysteryObject setY: 0.0]; [mysteryObject setZ: 7.5]; // Deconectați linia următoare pentru a vedea porțiunea "else" a condiționată. // mysteryObject = [[Alocare navă] init]; dacă [[mysteryObject conformsToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "Ok să-și asume suportul de coordonate."); NSLog (@ "Obiectul este situat la (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]); altceva NSLog (@ "Eroare: Nu este sigur să-și asume suportul de coordonate."); NSLog (@ "Nu am idee unde este obiectul acela ..."); return 0;
Dacă deconectați linia care reassignează mysteryObject
la a Navă
de exemplu, conformsToProtocol:
metoda va reveni NU
, și nu veți putea folosi în siguranță API-ul definit de CoordinateSupport
. Dacă nu sunteți sigur ce fel de obiect va avea o variabilă, acest tip de verificare dinamică a protocoalelor este important pentru a preveni coborârea programului dvs. atunci când încercați să apelați o metodă care nu există.
De asemenea, observați noul @protocol()
directivă. Aceasta funcționează mult @selector ()
, cu excepția unui nume de metodă, este nevoie de un nume de protocol. Se întoarce a Protocol
obiect, la care se poate trece conformsToProtocol:
, printre alte metode încorporate. Se face fișierul de antet al protocolului nu trebuie să fie importate pentru @protocol()
a munci.
Dacă ați terminat să lucrați cu multe protocoale, veți ajunge în cele din urmă într-o situație în care două protocoale se bazează unul pe altul. Această relație circulară reprezintă o problemă pentru compilator, deoarece nu poate importa cu succes nici una dintre ele fără celălalt. De exemplu, să presupunem că am încercat să eliminăm unele funcții GPS într-o GPSSupport
protocol, dar doriți să puteți converti între coordonatele "normale" ale celor existente CoordinateSupport
și coordonatele utilizate de către GPSSupport
. GPSSupport
protocolul este destul de simplu:
#import#import "CoordinateSupport.h" @protocol GPSSupport - (Void) copyCoordinatesFromObject: (id )obiectul; @Sfârșit
Acest lucru nu ridică probleme, adică până când nu trebuie să menționăm GPSSupport
protocol de la CoordinateSupport.h
:
#import#import "GPSSupport.h" @protocol CoordinateSupport @property dublu x; @property dublu y; @property double z; - (NSArray *) arrayFromPoziție; - magnitudinea (dublă); - (dublu) getDistanceFromObject: (id )obiectul; - (void) copyGPSCoordinatesFromObject: (id )obiectul; @Sfârșit
Acum CoordinateSupport.h
fișierul necesită GPSSupport.h
fișier pentru a compila corect și invers. Este o problemă de pui sau ouă și compilatorul nu-i va place foarte mult:
Rezolvarea relației recursive este simplă. Tot ce trebuie să faceți este să declanșați unul dintre protocoale în loc să încercați să le importați direct:
#import@protocol CoordinateSupport; @protocol GPSSupport - (Void) copyCoordinatesFromObject: (id )obiectul; @Sfârșit
Toate @protocol CoordinateSupport;
spune asta CoordinateSupport
este într-adevăr un protocol și compilatorul poate presupune că există fără a le importa. Observați punct și virgulă la sfârșitul instrucțiunii. Acest lucru se poate face în oricare dintre cele două protocoale; punctul este acela de a elimina referința circulară. Compilatorul nu-i pasă cum îl faci.
Protocoalele sunt o caracteristică incredibil de puternică a obiectivului C. Ele vă permit să capturați relațiile dintre clasele arbitrare atunci când nu este posibil să le conectați la o clasă părinte comună. Vom utiliza mai multe protocoale încorporate în iOS Succinct, deoarece multe dintre funcțiile de bază ale unei aplicații iPhone sau iPad sunt definite ca protocoale.
Capitolul următor prezintă excepții și erori, două instrumente foarte importante pentru gestionarea problemelor care apar în mod inevitabil în timp ce se scriu programe Obiectiv-C.
Această lecție reprezintă un capitol din Obiectiv-C Succinct, o carte electronică gratuită de la echipa de la Syncfusion.