Mål-C Succinctly Protokoller

I Mål-C, a protokollen er en gruppe metoder som kan implementeres av enhver klasse. Protokoller er i det vesentlige det samme som grensesnitt i C #, og de har begge like mål. De kan brukes som en pseudodatatype, som er nyttig for å sikre at en dynamisk skrevet objekt kan svare på et bestemt sett med meldinger. Og fordi noen klasser kan "adoptere" en protokoll, kan de brukes til å representere en delt API mellom helt uavhengige klasser.

Den offisielle dokumentasjonen diskuterer både en uformell og en formell metode for å erklære protokoller, men uformelle protokoller er egentlig bare en unik bruk av kategorier og gir ikke nesten like mange fordeler som formelle protokoller. Med dette i bakhodet fokuserer dette kapitlet utelukkende på formell protokoller.


Opprette en protokoll

Først, la oss ta en titt på hvordan å erklære en formell protokoll. Opprett en ny fil i Xcode og velg Objective-C-protokollikonet under Mac OS X> Kakao:

Xcode-ikon for protokollfiler

Som vanlig vil dette be deg om et navn. Vår protokoll inneholder metoder for å beregne koordinatene til et objekt, så la oss kalle det CoordinateSupport:

Oppgi protokollen

Klikk neste og velg standardplassering for filen. Dette vil opprette en tom protokoll som ser nesten ut som et grensesnitt:

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

Selvfølgelig, i stedet for @interface direktivet, bruker den @protocol, etterfulgt av protokollnavnet. De syntaks lar oss inkorporere en annen protokoll i CoordinateSupport. I dette tilfellet sier vi det CoordinateSupport Inkluderer også alle metodene deklarert i NSObject protokoll (ikke forveksles med NSObject klasse).

Deretter la vi legge til noen få metoder og egenskaper til protokollen. Dette fungerer på samme måte som deklarerer metoder og egenskaper i et grensesnitt:

#importere  @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (dobbel) størrelse; @slutt

Vedta en protokoll

Enhver klasse som vedtar denne protokollen er garantert å syntetisere x, y, og z egenskaper og implementere arrayFromPosition og omfanget metoder. Mens dette ikke sier hvordan de vil bli implementert, det gir deg muligheten til å definere en delt API for et vilkårlig sett med klasser.

For eksempel, hvis vi vil ha begge Skip og Person For å kunne svare på disse egenskapene og metodene, kan vi fortelle dem å vedta protokollen ved å plassere den i vinklede parenteser etter superklasserklæringen. Vær også oppmerksom på at, akkurat som å bruke en annen klasse, må du importere protokollfilen før du bruker den:

#importere  #import "CoordinateSupport.h" @interface Person: NSObject  @property (copy) NSString * navn; @property (strong) NSMutableSet * venner; - (void) sayHello; - (void) sayGoodbye; @slutt

Nå, i tillegg til egenskapene og metodene som er definert i dette grensesnittet, Person klassen er garantert å svare på API definert av CoordinateSupport. Xcode vil advare deg om at Person Implementeringen er ufullstendig til du syntetiserer x, y, og z, og implementere arrayFromPosition og omfanget:

Ufullstendig implementeringsadvarsel for Person

På samme måte kan en kategori vedta en protokoll ved å legge den til etter kategorien. For eksempel, for å fortelle Person klasse for å adoptere CoordinateSupport protokoll i relasjoner kategori, vil du bruke følgende linje:

@ Interface Person (Forhold) 

Og hvis klassen din trenger å adoptere mer enn en protokoll, kan du skille dem fra kommaer:

@interface Person: NSObject 

Fordeler med protokoller

Uten protokoller ville vi ha to alternativer for å sikre begge Skip og Person implementert denne delte API:

  1. Re-deklarere nøyaktig samme egenskaper og metoder i begge grensesnitt.
  2. Definer API i en abstrakt superklasse og definer Skip og Person som underklasser.

Ingen av disse alternativene er spesielt tiltalende: den første er overflødig og utsatt for menneskelig feil, og den andre er svært begrenset, spesielt hvis de allerede arver fra forskjellige foreldreklasser. Det bør være klart at protokollene er mye mer fleksible og gjenbrukbare, da de beskytter API-en fra å være avhengig av en bestemt klasse.

Det faktum at noen klassen kan enkelt adoptere en protokoll gjør det mulig å definere horisontale relasjoner på toppen av et eksisterende klassehierarki:

Koble urelaterte klasser med en protokoll

På grunn av protokollens fleksible karakter, gjør de ulike IOS-rammene en god bruk av dem. For eksempel er brukergrensesnittkontroller ofte konfigurert ved hjelp av delegasjonsdesignmønsteret, der et delegatobjekt er ansvarlig for å reagere på brukerhandlinger. I stedet for å inkapslere en delegents ansvar i en abstrakt klasse og tvinge delegater til å underklassere det, definerer iOS den nødvendige API for delegaten i en protokoll. På denne måten er det utrolig enkelt for noen motsette seg å fungere som delegatobjektet. Vi vil utforske dette i mye mer detalj i andre halvdel av denne serien, iOS Succinctly.


Protokoller som pseudotyper

Protokoller kan brukes som psuedo-datatyper. I stedet for å sørge for at en variabel er en forekomst av en klasse, sørger det for at variabelen alltid overholder en vilkårlig API ved hjelp av en protokoll som et typekontrollverktøy. For eksempel, følgende person variabel er garantert å implementere CoordinateSupport API.

Person  * person = [[Personallokering] init];

Likevel, håndheving av protokollannonsering er ofte mer nyttig når den brukes sammen med id data-type. Dette lar deg anta visse metoder og egenskaper mens du helt ignorerer objektets klasse.

Og selvfølgelig kan samme syntaks brukes med en metodeparameter. Følgende utdrag legger til en ny getDistanceFromObject: metode til API som parameteren er nødvendig for å overholde CoordinateSupport protokoll:

// CoordinateSupport.h #import  @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (dobbel) størrelse; - (dobbelt) getDistanceFromObject: (id )objektet; @slutt

Vær oppmerksom på at det er helt mulig å bruke en protokoll i samme fil som den er definert.

Kontroll av dynamisk overensstemmelse

I tillegg til den statiske typen sjekken som er omtalt i det siste avsnittet, kan du også bruke conformsToProtocol: metode definert av NSObject protokoll for å sjekke om en objekt samsvarer med en protokoll eller ikke. Dette er nyttig for å forhindre feil når du arbeider med dynamiske objekter (objekter skrevet som id).

Følgende eksempel antar Person klassen vedtar CoordinateSupport protokoll, mens Skip klassen gjør det ikke. Den bruker et dynamisk skrevet objekt som heter mysteryObject å lagre en forekomst av Person,og bruker deretter conformsToProtocol: for å sjekke om den har koordinatstøtte. Hvis det gjør det, er det trygt å bruke x, y, og z egenskaper, samt de andre metodene deklarert i CoordinateSupport protokoll:

// main.m #import  #import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @ autoreleasepool id mysteryObject = [[Personallokering] init]; [mysteryObject setX: 10.0]; [mysteryObject setY: 0.0]; [mysteryObject setZ: 7.5]; // Uncomment neste linje for å se "ellers" delen av betinget. // mysteryObject = [[Ship alloc] init]; hvis ([mysteryObject conformsToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "Ok for å anta koordinatstøtte."); NSLog (@ "Objektet ligger på (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]);  ellers NSLog (@ "Feil: Ikke trygt å anta koordinatstøtte."); NSLog (@ "Jeg aner ikke hvor objektet er ...");  returnere 0; 

Hvis du uncomment linjen som tilordnes mysteryObject til en Skip eksempel, conformsToProtocol: metoden kommer tilbake NEI, og du vil ikke kunne bruke APIen som er definert av CoordinateSupport. Hvis du ikke er sikker på hva slags objekt en variabel vil holde, er denne typen dynamisk protokollkontroll viktig for å forhindre at programmet krasjer når du prøver å kalle en metode som ikke eksisterer.

Legg også merke til det nye @protocol () direktiv. Dette fungerer mye som @selector (), bortsett fra i stedet for et metodenavn, tar det et protokollnavn. Den returnerer a protokoll objekt, som kan sendes til conformsToProtocol:, blant annet innebygde metoder. Protokollhovedfilen gjør det ikke må importeres for @protocol () å jobbe.


Forward-Declaration Protocols

Hvis du ender med å jobbe med mange protokoller, kommer du til slutt i en situasjon der to protokoller stole på hverandre. Dette sirkulære forholdet utgjør et problem for kompilatoren, siden det ikke kan importere noen av dem uten den andre. For eksempel, la oss si at vi prøvde å trekke ut noen GPS-funksjonalitet til en GPSSupport protokollen, men vil kunne konvertere mellom de "normale" koordinatene til vår eksisterende CoordinateSupport og koordinatene som brukes av GPSSupport. De GPSSupport protokollen er ganske enkel:

#importere  #import "CoordinateSupport.h" @protocol GPSSupport  - (Void) copyCoordinatesFromObject: (id )objektet; @slutt

Dette utgjør ingen problemer, det vil si til vi må referere til GPSSupport protokoll fra CoordinateSupport.h:

#importere  #import "GPSSupport.h" @protocol CoordinateSupport  @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (dobbel) størrelse; - (dobbelt) getDistanceFromObject: (id )objektet; - (ugyldig) copyGPSCoordinatesFromObject: (id )objektet; @slutt

Nå, den CoordinateSupport.h filen krever GPSSupport.h fil for å kompilere riktig, og omvendt. Det er en kylling-eller-the-egg slags problem, og kompilatoren vil ikke like det veldig mye:

Kompilatorfeil forårsaket av sirkulære protokollreferanser

Å løse det rekursive forholdet er enkelt. Alt du trenger å gjøre er å forklare en av protokollene i stedet for å prøve å importere den direkte:

#importere  @protocol CoordinateSupport; @protocol GPSSupport  - (Void) copyCoordinatesFromObject: (id )objektet; @slutt

Alle @protocol CoordinateSupport; sier det CoordinateSupport er faktisk en protokoll og kompilatoren kan anta at den eksisterer uten å importere den. Legg merke til semikolonet på slutten av setningen. Dette kan gjøres i en av de to protokollene; poenget er å fjerne den sirkulære referansen. Kompilatoren bryr seg ikke hvordan du gjør det.


Sammendrag

Protokoller er en utrolig kraftig funksjon av Objective-C. De lar deg fange forhold mellom vilkårlig klasser når det ikke er mulig å koble dem til en felles foreldreklasse. Vi bruker flere innebygde protokoller i iOS Succinctly, så mange av kjernefunksjonene til en iPhone eller iPad app er definert som protokoller.

I neste kapittel presenteres unntak og feil, to meget viktige verktøy for å håndtere problemene som uunngåelig oppstår under skriving av Objective-C programmer.

Denne leksjonen representerer et kapittel fra Objective-C Succinctly, en gratis eBok fra teamet på Syncfusion.