Objectief-C bondig protocollen

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.


Een protocol maken

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 protocolbestanden

Zoals 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 geven

Klik 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

Een protocol goedkeuren

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:

Onvolledige implementatiewaarschuwing voor persoon

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 

Voordelen van protocollen

Zonder protocollen zouden we twee opties hebben om beide te waarborgen Schip en Persoon deze gedeelde API geïmplementeerd:

  1. Verklaar exact dezelfde eigenschappen en methoden opnieuw in beide interfaces.
  2. Definieer de API in een abstracte superklasse en definieer 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 protocol

Vanwege 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 als pseudo-typen

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.

Dynamic Conformance Checking

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.


Protocollen voor vooruitmelding

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:

Compileerfout veroorzaakt door circulaire protocolreferenties

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.


Samenvatting

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.