I mål-C, a protokoll är en grupp metoder som kan genomföras av någon klass. Protokoll är i huvudsak desamma som gränssnitt i C #, och de båda har liknande mål. De kan användas som en pseudodatatyp, vilket är användbart för att se till att ett dynamiskt typat objekt kan svara på en viss uppsättning meddelanden. Och eftersom någon klass kan "anta" ett protokoll kan de användas för att representera ett delat API mellan helt orelaterade klasser.
Den officiella dokumentationen diskuterar både en informell och en formell metod för att deklarera protokoll, men informella protokoll är egentligen bara en unik användning av kategorier och ger inte nästan lika många fördelar som formella protokoll. Med detta i åtanke fokuserar detta kapitel uteslutande på formell protokoll.
Låt oss först titta på hur man förklarar ett formellt protokoll. Skapa en ny fil i Xcode och välj objektiv-C-protokollikonen under Mac OS X> Kakao:
Xcode-ikonen för protokollfilerSom vanligt kommer det att leda till ett namn. Vårt protokoll innehåller metoder för att beräkna ett objekts koordinater, så låt oss ringa det CoordinateSupport:
Namngivna protokolletKlick Nästa och välj standardplatsen för filen. Detta skapar ett tomt protokoll som ser nästan ut som ett gränssnitt:
// CoordinateSupport.h #import@protocol CoordinateSupport @slutet
Självklart, istället för @gränssnitt
direktivet använder den @protokoll
, följt av protokollets namn. De
syntax låter oss införliva ett annat protokoll till CoordinateSupport
. I det här fallet säger vi det CoordinateSupport
innehåller också alla metoder som deklarerats i NSObject
protokoll (inte förväxlas med NSObject
klass).
Låt oss sedan lägga till några metoder och egenskaper för protokollet. Detta fungerar på samma sätt som deklarerar metoder och egenskaper i ett gränssnitt:
#importera@protocol CoordinateSupport @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (dubbel) magnitude; @slutet
Alla klasser som antar detta protokoll garanteras att syntetisera x
, y
, och z
egenskaper och implementera arrayFromPosition
och magnitud
metoder. Även om detta inte säger på vilket sätt de kommer att genomföras, det ger dig möjlighet att definiera ett delat API för en godtycklig uppsättning klasser.
Till exempel, om vi vill ha båda Fartyg
och Person
För att kunna svara på dessa egenskaper och metoder kan vi berätta för dem att anta protokollet genom att placera det i vinklade fästen efter superklassdeklarationen. Observera också att du, precis som att använda en annan klass, måste importera protokollfilen innan du använder den:
#importera#import "CoordinateSupport.h" @interface Person: NSObject @property (copy) NSString * namn; @property (strong) NSMutableSet * vänner; - (tom) sägHello; - (tomrum) sägGodbye; @slutet
Nu, förutom de egenskaper och metoder som definieras i detta gränssnitt, Person
klassen är garanterad att svara på API definierat av CoordinateSupport
. Xcode kommer att varna dig att Person
Implementeringen är ofullständig tills du syntetiserar x
, y
, och z
, och implementera arrayFromPosition
och magnitud
:
På samma sätt kan en kategori anta ett protokoll genom att lägga till det efter kategorin. Till exempel, berätta för Person
klass för att anta CoordinateSupport
protokoll i relationer
kategori, skulle du använda följande rad:
@interface Person (Relationer)
Och om din klass måste anta mer än ett protokoll kan du skilja dem med kommatecken:
@interface Person: NSObject
Utan protokoll skulle vi ha två alternativ för att säkerställa båda Fartyg
och Person
genomförde detta delade API:
Fartyg
och Person
som underklasser.Ingen av dessa alternativ är särskilt tilltalande: den första är överflödig och benägen för mänskliga fel, och den andra är allvarligt begränsande, särskilt om de redan ärver från olika föräldrakurser. Det bör vara klart att protokoll är mycket mer flexibla och återanvändbara, eftersom de skyddar API: n från att vara beroende av en viss klass.
Faktumet att några klassen kan enkelt adoptera ett protokoll gör det möjligt att definiera horisontella relationer ovanpå en befintlig klasshierarki:
Länka orelaterade klasser med ett protokollPå grund av protokollens flexibla karaktär gör de olika iOS-ramarna en bra användning av dem. Till exempel konfigureras användargränssnittskontroller ofta med hjälp av delegationsdesignmönstret, där ett delegatobjekt ansvarar för att reagera på användaråtgärder. I stället för att inkapslera en delegats ansvar i en abstrakt klass och tvinga delegater att delklassificera den, definierar iOS det nödvändiga API för delegaten i ett protokoll. På så sätt är det otroligt enkelt för några föremål för att fungera som delegatobjektet. Vi kommer att undersöka det här i mycket mer detalj i andra halvan av denna serie, IOS Succinctly.
Protokoll kan användas som psuedo-datatyper. I stället för att se till att en variabel är en förekomst av en klass, använder ett protokoll som ett typkontrollverktyg att variabeln alltid överensstämmer med ett godtyckligt API. Till exempel följande person
variabel garanteras att implementera CoordinateSupport API.
Person* person = [[Personallokering] init];
Det är dock ofta mer användbart att tillämpa protokollupptagning när den används tillsammans med id
data typ. Detta låter dig anta vissa metoder och egenskaper samtidigt som objektet är helt bortse från objektets klass.
Och naturligtvis kan samma syntax användas med en metodparameter. Följande kod lägger till en ny getDistanceFromObject:
Metod till API vars parameter krävs för att överensstämma med CoordinateSupport
protokoll:
// CoordinateSupport.h #import@protocol CoordinateSupport @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (dubbel) magnitude; - (dubbel) getDistanceFromObject: (id )objektet; @slutet
Observera att det är helt möjligt att använda ett protokoll i samma fil som den definieras.
Förutom den statiska typkontroll som beskrivs i det sista avsnittet kan du också använda conformsToProtocol:
Metod definierad av NSObject
protokoll för att dynamiskt kontrollera om ett objekt överensstämmer med ett protokoll eller inte. Detta är användbart för att förhindra fel när du arbetar med dynamiska objekt (objekt som skrivs som id
).
Följande exempel förutsätter Person
klass adopterar CoordinateSupport
protokoll, medan Fartyg
klassen gör det inte. Det använder ett dynamiskt typat objekt som heter mysteryObject
att lagra en förekomst av Person
,och använder sedan conformsToProtocol:
för att kontrollera om det har koordinatstöd. Om det gör det är det säkert att använda x
, y
, och z
egenskaper, liksom de andra metoder som deklarerats 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 nästa rad för att se "annars" delen av villkorat. // mysteryObject = [[Ship alloc] init]; om ([mysteryObject conformsToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "Ok att anta koordinatsupport."); NSLog (@ "Objektet ligger på (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]); else NSLog (@ "Fel: Ej säkert att anta koordinatsupport."); NSLog (@ "Jag har ingen aning om var objektet är ..."); returnera 0;
Om du inte kommenterar linjen som omfördelas mysteryObject
till a Fartyg
Exempel på conformsToProtocol:
Metoden kommer att återvända NEJ
, och du kommer inte att kunna använda det API som definieras av CoordinateSupport
. Om du inte är säker på vilken typ av objekt en variabel kommer att hålla, är denna typ av dynamisk protokollkontroll viktigt för att förhindra att programmet kraschar när du försöker ringa en metod som inte existerar.
Observera också det nya @protokoll()
direktiv. Det fungerar så mycket @väljare()
, utom i stället för ett metodnamn, tar det ett protokollnamn. Den returnerar a Protokoll
objekt som kan vidarebefordras till conformsToProtocol:
, bland annat inbyggda metoder. Protokollhuvudfilen gör det inte måste importeras till @protokoll()
att jobba.
Om du slutar arbeta med många protokoll kommer du så småningom att gå in i en situation där två protokoll är beroende av varandra. Detta cirkulära förhållande utgör ett problem för kompilatorn, eftersom det inte framgångsrikt kan importera någon av dem utan den andra. Låt oss till exempel säga att vi försökte abstrahera vissa GPS-funktionaliteter till en GPSSupport
protokoll, men vill kunna konvertera mellan "normala" koordinater för vår befintliga CoordinateSupport
och koordinaterna som används av GPSSupport
. De GPSSupport
protokollet är ganska enkelt:
#importera#import "CoordinateSupport.h" @protocol GPSSupport - (Void) copyCoordinatesFromObject: (id )objektet; @slutet
Detta ställer inga problem, det vill säga tills vi behöver referera till GPSSupport
protokoll från CoordinateSupport.h
:
#importera#import "GPSSupport.h" @protocol CoordinateSupport @property double x; @property double y; @property double z; - (NSArray *) arrayFromPosition; - (dubbel) magnitude; - (dubbel) getDistanceFromObject: (id )objektet; - (void) copyGPSCoordinatesFromObject: (id )objektet; @slutet
Nu den CoordinateSupport.h
filen kräver GPSSupport.h
filen för att kompilera korrekt och vice versa. Det är en kyckling-eller-ägg-typ av problem, och kompilatorn tycker inte om det väldigt mycket:
Att lösa det rekursiva förhållandet är enkelt. Allt du behöver göra är att förklara ett av protokollen istället för att försöka importera det direkt:
#importera@protocol CoordinateSupport; @protocol GPSSupport - (Void) copyCoordinatesFromObject: (id )objektet; @slutet
Allt @protocol CoordinateSupport;
säger det CoordinateSupport
är verkligen ett protokoll och kompilatorn kan anta att det existerar utan att importera det. Notera semikolon i slutet av uttalandet. Detta kan göras i någon av de två protokollen; Poängen är att ta bort den cirkulära referensen. Kompilatorn bryr sig inte om hur du gör det.
Protokoll är en oerhört kraftfull funktion av Objective-C. De låter dig fånga relationer mellan godtyckliga klasser när det inte är möjligt att ansluta dem med en gemensam föräldraklass. Vi använder flera inbyggda protokoll i IOS Succinctly, så många av kärnfunktionerna i en iPhone eller iPad app definieras som protokoll.
I nästa kapitel introduceras undantag och fel, två mycket viktiga verktyg för att hantera de problem som oundvikligen uppstår när man skriver mål-C-program.
Denna lektion representerar ett kapitel från Objective-C Succinctly, en gratis eBook från laget vid Syncfusion.