In Objective-C, a protocol is een groep methoden die door elke klasse kan worden geïmplementeerd. Protocollen zijn in wezen hetzelfde als interfaces in C #, en ze hebben allebei vergelijkbare doelen. Ze kunnen worden gebruikt als een pseudo-gegevenstype, wat handig is om ervoor te zorgen dat een dynamisch getypt object kan reageren op een bepaalde reeks berichten. En omdat elke klasse een protocol kan 'adopteren', kunnen ze worden gebruikt om een gedeelde API te vertegenwoordigen tussen volledig niet-gerelateerde klassen.
De officiële documentatie bespreekt zowel een informele als een formele methode voor het declareren van protocollen, maar informele protocollen zijn eigenlijk gewoon een uniek gebruik van categorieën en bieden niet zo veel voordelen als formele protocollen. Met dit in gedachten richt dit hoofdstuk zich uitsluitend op formeel protocollen.
Laten we eerst eens kijken hoe we een formeel protocol kunnen declareren. Maak een nieuw bestand in Xcode en selecteer hieronder het pictogram Objectief-C-protocol Mac OS X> Cocoa:
Xcode-pictogram voor protocolbestandenZoals gewoonlijk zal dit u om een naam vragen. Ons protocol bevat methoden voor het berekenen van de coördinaten van een object, dus laten we het noemen CoordinateSupport:
Het protocol een naam gevenKlik volgende en kies de standaardlocatie voor het bestand. Hiermee wordt een leeg protocol gemaakt dat er bijna precies uitziet als een interface:
// CoordinateSupport.h #import@protocol CoordinateSupport @einde
Natuurlijk, in plaats van de @interface
richtlijn, het gebruikt @protocol
, gevolgd door de protocolnaam. De
syntaxis laat ons een ander protocol opnemen CoordinateSupport
. In dit geval zeggen we dat CoordinateSupport
omvat ook alle methoden die zijn gedeclareerd in de NSObject
protocol (niet te verwarren met de NSObject
klasse).
Laten we vervolgens een paar methoden en eigenschappen aan het protocol toevoegen. Dit werkt op dezelfde manier als het verklaren van methoden en eigenschappen in een interface:
#importeren@protocol CoordinateSupport @eigerty double x; @property double y; @eigerty double z; - (NSArray *) arrayFromPosition; - (dubbele) magnitude; @einde
Elke klasse die dit protocol gebruikt, is gegarandeerd de synthese X
, Y
, en z
eigenschappen en implementeer de arrayFromPosition
en omvang
methoden. Hoewel dit niet zegt hoe ze worden geïmplementeerd, het geeft je de mogelijkheid om een gedeelde API te definiëren voor een willekeurige reeks klassen.
Bijvoorbeeld, als we beide willen Schip
en Persoon
om te kunnen reageren op deze eigenschappen en methoden, kunnen we hen vertellen het protocol over te nemen door het in schuine haken te plaatsen na de verklaring van de superclass. Merk ook op dat u, net als bij het gebruik van een andere klasse, het protocolbestand moet importeren voordat u het gebruikt:
#importeren#import "CoordinateSupport.h" @interface Persoon: NSObject @property (kopie) NSString * naam; @property (sterk) NSMutableSet * vrienden; - (ongeldig) zeg Hallo; - (void) sayGoodbye; @einde
Nu, naast de eigenschappen en methoden die in deze interface zijn gedefinieerd, is de Persoon
klasse is gegarandeerd om te reageren op de API gedefinieerd door CoordinateSupport
. Xcode waarschuwt u dat de Persoon
de implementatie is onvolledig totdat je synthetiseert X
, Y
, en z
, en implementeren arrayFromPosition
en omvang
:
Evenzo kan een categorie een protocol adopteren door het na de categorie toe te voegen. Bijvoorbeeld om het te vertellen Persoon
klasse om het te adopteren CoordinateSupport
protocol in de Relaties
categorie, zou je de volgende regel gebruiken:
@interface Persoon (relaties)
En als uw klas meer dan één protocol moet gebruiken, kunt u ze scheiden met komma's:
@interface Persoon: NSObject
Zonder protocollen zouden we twee opties hebben om beide te waarborgen Schip
en Persoon
deze gedeelde API geïmplementeerd:
Schip
en Persoon
als subklassen.Geen van deze opties is bijzonder aantrekkelijk: de eerste is overbodig en vatbaar voor menselijke fouten, en de tweede is ernstig beperkend, vooral als ze al erven van verschillende ouderklassen. Het moet duidelijk zijn dat protocollen veel flexibeler en herbruikbaarder zijn, omdat ze de API beschermen tegen afhankelijk zijn van een bepaalde klasse.
Het feit dat ieder klasse kan eenvoudig een protocol adopteren maakt het mogelijk om horizontale relaties te definiëren bovenop een bestaande klassehiërarchie:
Niet-gerelateerde klassen koppelen met behulp van een protocolVanwege het flexibele karakter van protocollen maken de verschillende iOS-frameworks daar goed gebruik van. Gebruikersinterfacebesturingselementen worden bijvoorbeeld vaak geconfigureerd met behulp van het delegatieontwerppatroon, waarbij een gedelegeerd object verantwoordelijk is voor het reageren op gebruikersacties. In plaats van de verantwoordelijkheden van een afgevaardigde in een abstracte klasse in te kapselen en deelnemers te subclasseren, definieert iOS de benodigde API voor de afgevaardigde in een protocol. Op deze manier is het ongelooflijk gemakkelijk ieder object om op te treden als het delegatieobject. We zullen dit in de tweede helft van deze serie in meer detail bekijken, iOS bondig.
Protocollen kunnen worden gebruikt als psuedo-gegevenstypen. In plaats van ervoor te zorgen dat een variabele een instantie van een klasse is, zorgt het gebruik van een protocol als een typecontroletool ervoor dat de variabele altijd overeenkomt met een willekeurige API. Bijvoorbeeld het volgende persoon
variabele is gegarandeerd de CoordinateSupport-API implementeren.
Persoon* persoon = [[Person alloc] init];
Toch is handhaving van protocoladoptie vaak handiger in combinatie met de ID kaart
data type. Hiermee kunt u bepaalde methoden en eigenschappen aannemen zonder de klasse van het object volledig te negeren.
En natuurlijk kan dezelfde syntaxis worden gebruikt met een parameter van de methode. In het volgende fragment wordt een nieuw fragment toegevoegd getDistanceFromObject:
methode voor de API waarvan de parameter moet voldoen aan CoordinateSupport
protocol:
// CoordinateSupport.h #import@protocol CoordinateSupport @eigerty double x; @property double y; @eigerty double z; - (NSArray *) arrayFromPosition; - (dubbele) magnitude; - (double) getDistanceFromObject: (id )het object; @einde
Merk op dat het heel goed mogelijk is om een protocol te gebruiken in hetzelfde bestand als het is gedefinieerd.
Naast de statische typecontrole die in de laatste sectie is besproken, kunt u ook de conformsToProtocol:
methode gedefinieerd door de NSObject
protocol om dynamisch te controleren of een object voldoet aan een protocol of niet. Dit is handig om fouten te voorkomen bij het werken met dynamische objecten (objecten die zijn getypt als ID kaart
).
In het volgende voorbeeld wordt de Persoon
klasse keurt het goed CoordinateSupport
protocol, terwijl de Schip
klasse doet dat niet. Het gebruikt een dynamisch getypt object genaamd mysteryObject
om een exemplaar van op te slaan Persoon
,en gebruikt vervolgens conformsToProtocol:
om te controleren of het coördinatiesteun heeft. Als dat zo is, is het veilig om de. Te gebruiken X
, Y
, en z
eigenschappen, evenals de andere methoden gedeclareerd in de CoordinateSupport
protocol:
// 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]; // Maak de volgende regel uncomment om het "else" -gedeelte van conditional te zien. // mysteryObject = [[Ship alloc] init]; if ([mysteryObject conformToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "Ok om gecoördineerde ondersteuning aan te nemen."); NSLog (@ "Het object bevindt zich op (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]); else NSLog (@ "Fout: niet veilig om ondersteuning voor coördinaten aan te nemen."); NSLog (@ "Ik heb geen idee waar dat object is ..."); retourneert 0;
Als u de regel uncomment die de toewijzing opnieuw toewijst mysteryObject
naar een Schip
bijvoorbeeld, de conformsToProtocol:
methode zal terugkeren NEE
, en je kunt de API gedefinieerd door. niet veilig gebruiken CoordinateSupport
. Als u niet zeker weet welk type object een variabele bevat, is dit soort dynamische protocolcontrole belangrijk om te voorkomen dat uw programma vastloopt wanneer u een methode probeert te gebruiken die niet bestaat.
Let ook op het nieuwe @protocol()
richtlijn. Dit werkt veel als @ selector ()
, behalve in plaats van de naam van een methode, is een protocolnaam vereist. Het levert a Protocol
object, waaraan kan worden doorgegeven conformsToProtocol:
, onder andere ingebouwde methoden. Het protocol header-bestand doet niet moet worden geïmporteerd voor @protocol()
werken.
Als je met veel protocollen werkt, kom je uiteindelijk in een situatie terecht waarin twee protocollen op elkaar zijn gebaseerd. Deze circulaire relatie vormt een probleem voor de compiler, omdat deze geen van beide succesvol kan importeren zonder de andere. Laten we zeggen dat we bijvoorbeeld een aantal GPS-functies in a wilden abstraheren GPSSupport
protocol, maar willen kunnen converteren tussen de "normale" coördinaten van ons bestaande CoordinateSupport
en de coördinaten gebruikt door GPSSupport
. De GPSSupport
protocol is vrij eenvoudig:
#importeren#import "CoordinateSupport.h" @protocol GPSSupport - (Void) copyCoordinatesFromObject: (id )het object; @einde
Dit levert geen problemen op, dat wil zeggen, totdat we moeten verwijzen naar de GPSSupport
protocol van CoordinateSupport.h
:
#importeren#import "GPSSupport.h" @protocol CoordinateSupport @eigerty double x; @property double y; @eigerty double z; - (NSArray *) arrayFromPosition; - (dubbele) magnitude; - (double) getDistanceFromObject: (id )het object; - (void) copyGPSCoordinatesFromObject: (id )het object; @einde
Nu de CoordinateSupport.h
bestand vereist de GPSSupport.h
bestand om correct te compileren, en vice versa. Het is een soort kip-of-ei-probleem, en de compiler bevalt het niet erg:
Het is eenvoudig om de recursieve relatie op te lossen. Het enige dat u hoeft te doen, is een van de protocollen doorsturen - in plaats van proberen het rechtstreeks te importeren:
#importeren@protocol CoordinateSupport; @protocol GPSSupport - (Void) copyCoordinatesFromObject: (id )het object; @einde
Allemaal @protocol CoordinateSupport;
zegt is dat CoordinateSupport
is inderdaad een protocol en de compiler kan aannemen dat het bestaat zonder het te importeren. Let op de puntkomma aan het einde van de instructie. Dit zou kunnen worden gedaan in een van de twee protocollen; het punt is om de kringvormige verwijzing te verwijderen. De compiler maakt niet uit hoe je het doet.
Protocollen zijn een ongelooflijk krachtig kenmerk van Objective-C. Ze laten je relaties tussen willekeurige klassen vastleggen wanneer het niet haalbaar is om ze te verbinden met een gemeenschappelijke ouderklasse. We gebruiken verschillende ingebouwde protocollen in iOS bondig, omdat veel van de kernfuncties van een iPhone- of iPad-app worden gedefinieerd als protocollen.
Het volgende hoofdstuk introduceert uitzonderingen en fouten, twee zeer belangrijke hulpmiddelen voor het beheren van de problemen die zich onvermijdelijk voordoen bij het schrijven van Objective-C-programma's.
Deze les vertegenwoordigt een hoofdstuk uit Objective-C bondig, een gratis eBoek van het team van Syncfusion.