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.
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 protokollfilerSom 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 protokollenKlikk 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
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
:
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
Uten protokoller ville vi ha to alternativer for å sikre begge Skip
og Person
implementert denne delte API:
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 protokollPå 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 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.
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.
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:
Å 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.
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.