In Ziel-C, a Protokoll ist eine Gruppe von Methoden, die von jeder Klasse implementiert werden können. Protokolle sind im Wesentlichen die gleichen wie Schnittstellen in C #, und beide haben ähnliche Ziele. Sie können als Pseudodatentyp verwendet werden. Dies ist hilfreich, um sicherzustellen, dass ein dynamisch typisiertes Objekt auf einen bestimmten Satz von Nachrichten reagieren kann. Da jede Klasse ein Protokoll "übernehmen" kann, kann sie verwendet werden, um eine gemeinsam genutzte API zwischen vollständig unabhängigen Klassen darzustellen.
In der offiziellen Dokumentation wird sowohl eine informelle als auch eine formale Methode zum Deklarieren von Protokollen beschrieben. Informelle Protokolle sind jedoch nur eine eindeutige Verwendung von Kategorien und bieten nicht annähernd so viele Vorteile wie formale Protokolle. In diesem Sinne konzentriert sich dieses Kapitel ausschließlich auf formal Protokolle.
Schauen wir uns zunächst an, wie ein formelles Protokoll deklariert wird. Erstellen Sie eine neue Datei in Xcode und wählen Sie das Symbol Objective-C-Protokoll unter Mac OS X> Kakao:
Xcode-Symbol für ProtokolldateienWie üblich werden Sie dazu aufgefordert, einen Namen einzugeben. Unser Protokoll enthält Methoden zur Berechnung der Koordinaten eines Objekts. Nennen wir es also CoordinateSupport:
Protokoll benennenKlicken Nächster und wählen Sie den Standardspeicherort für die Datei. Dadurch wird ein leeres Protokoll erstellt, das fast genau wie eine Schnittstelle aussieht:
// CoordinateSupport.h #import@protocol CoordinateSupport @Ende
Natürlich statt der @Schnittstelle
Direktive verwendet es @Protokoll
, gefolgt vom Protokollnamen. Das
Syntax lässt uns ein anderes Protokoll in integrieren CoordinateSupport
. In diesem Fall sagen wir das CoordinateSupport
schließt auch alle in der deklarierten Methoden ein NSObject
Protokoll (nicht mit dem verwechseln NSObject
Klasse).
Als Nächstes fügen wir dem Protokoll einige Methoden und Eigenschaften hinzu. Dies funktioniert genauso wie das Deklarieren von Methoden und Eigenschaften in einer Schnittstelle:
#einführen@protocol CoordinateSupport @ property double x; @ property double y; @ property double z; - (NSArray *) arrayFromPosition; - (doppelte) Größenordnung; @Ende
Jede Klasse, die dieses Protokoll verwendet, synthetisiert garantiert das x
, y
, und z
Eigenschaften und implementieren die arrayFromPosition
und Größe
Methoden. Während dies nicht sagt Wie Wenn sie implementiert werden, haben Sie die Möglichkeit, eine gemeinsame API für eine beliebige Gruppe von Klassen zu definieren.
Zum Beispiel, wenn wir beides wollen Schiff
und Person
Um auf diese Eigenschaften und Methoden reagieren zu können, können Sie ihnen sagen, dass sie das Protokoll übernehmen sollen, indem Sie es nach der Deklaration der Oberklasse in spitze Klammern setzen. Beachten Sie auch, dass Sie genau wie eine andere Klasse die Protokolldatei importieren müssen, bevor Sie sie verwenden können:
#einführen#import "CoordinateSupport.h" @interface Person: NSObject @ property (copy) NSString * name; @ property (strong) NSMutableSet * Freunde; - (nichtig) sagen hallo; - (nichtig) sagen, auf Wiedersehen; @Ende
Jetzt zusätzlich zu den in dieser Schnittstelle definierten Eigenschaften und Methoden die Person
Die Klasse reagiert garantiert auf die von CoordinateSupport
. Xcode wird Sie darauf hinweisen Person
Die Implementierung ist unvollständig, bis Sie synthetisiert haben x
, y
, und z
, und umsetzen arrayFromPosition
und Größe
:
Ebenso kann eine Kategorie ein Protokoll übernehmen, indem es nach der Kategorie hinzugefügt wird. Zum Beispiel, um das zu sagen Person
Klasse, um die CoordinateSupport
Protokoll in der Beziehungen
Kategorie würden Sie die folgende Zeile verwenden:
@Interface Person (Beziehungen)
Wenn Ihre Klasse mehr als ein Protokoll übernehmen muss, können Sie sie mit Kommas trennen:
@Interface Person: NSObject
Ohne Protokolle hätten wir zwei Möglichkeiten, um beides sicherzustellen Schiff
und Person
Diese gemeinsame API wurde implementiert:
Schiff
und Person
als Unterklassen.Keine dieser Optionen ist besonders ansprechend: Die erste ist redundant und anfällig für menschliches Versagen, und die zweite ist stark einschränkend, insbesondere wenn sie bereits von verschiedenen Elternklassen erben. Es sollte klar sein, dass Protokolle viel flexibler und wiederverwendbar sind, da sie die API vor einer Abhängigkeit von einer bestimmten Klasse schützen.
Die Tatsache, dass irgendein Klasse kann ein Protokoll leicht übernehmen, um horizontale Beziehungen über einer vorhandenen Klassenhierarchie zu definieren:
Verknüpfen nicht verbundener Klassen mithilfe eines ProtokollsAufgrund der flexiblen Natur der Protokolle werden sie von den verschiedenen iOS-Frameworks gut genutzt. Beispielsweise werden Benutzeroberflächen-Steuerelemente häufig unter Verwendung des Delegierungsentwurfsmusters konfiguriert, wobei ein Delegatenobjekt für die Reaktion auf Benutzeraktionen verantwortlich ist. Anstatt die Verantwortlichkeiten eines Delegaten in einer abstrakten Klasse zu kapseln und die Delegierten zu einer Unterklasse zu zwingen, definiert iOS die erforderliche API für den Delegierten in einem Protokoll. Auf diese Weise ist es unglaublich einfach für irgendein Objekt, das als Delegatobjekt fungiert. Wir werden dies in der zweiten Hälfte dieser Serie genauer untersuchen, iOS Prägnant.
Protokolle können als Pseudo-Datentypen verwendet werden. Anstatt sicherzustellen, dass eine Variable eine Instanz einer Klasse ist, stellt die Verwendung eines Protokolls als Typüberprüfungstool sicher, dass die Variable immer einer beliebigen API entspricht. Zum Beispiel folgendes Person
Die Implementierung der CoordinateSupport-API ist garantiert.
Person* Person = [[Personenzuweisung] Init];
Trotzdem ist die Durchsetzung von Protokollen in Verbindung mit der Ich würde
Datentyp. Dadurch können Sie bestimmte Methoden und Eigenschaften annehmen, ohne die Klasse des Objekts vollständig zu ignorieren.
Natürlich kann dieselbe Syntax auch mit einem Methodenparameter verwendet werden. Der folgende Ausschnitt fügt ein neues hinzu getDistanceFromObject:
Methode an die API, deren Parameter erfüllt werden muss CoordinateSupport
Protokoll:
// CoordinateSupport.h #import@protocol CoordinateSupport @ property double x; @ property double y; @ property double z; - (NSArray *) arrayFromPosition; - (doppelte) Größenordnung; - (doppelt) getDistanceFromObject: (id )das Objekt; @Ende
Beachten Sie, dass es durchaus möglich ist, ein Protokoll in derselben Datei zu verwenden, in der es definiert ist.
Zusätzlich zur statischen Typprüfung, die im letzten Abschnitt beschrieben wurde, können Sie auch die verwenden conformsToProtocol:
Methode definiert durch die NSObject
Protokoll, um dynamisch zu prüfen, ob ein Objekt einem Protokoll entspricht oder nicht. Dies ist nützlich, um Fehler bei der Arbeit mit dynamischen Objekten (als eingegebenen Objekten) zu vermeiden Ich würde
).
Im folgenden Beispiel wird davon ausgegangen, dass Person
Klasse übernimmt die CoordinateSupport
Protokoll, während die Schiff
Klasse tut nicht. Es verwendet ein dynamisch typisiertes Objekt namens mysteryObject
eine Instanz von speichern Person
,und verwendet dann conformsToProtocol:
um zu prüfen, ob es eine koordinierte Unterstützung hat. Wenn dies der Fall ist, können Sie die x
, y
, und z
Eigenschaften sowie die anderen in der CoordinateSupport
Protokoll:
// main.m #import#import "Person.h" #import "Ship.h" int main (int argc, const char * argv []) @autoreleasepool id mysteryObject = [[Personallokation] init]; [mysteryObject setX: 10.0]; [mysteryObject setY: 0.0]; [mysteryObject setZ: 7.5]; // Kommentieren Sie die nächste Zeile, um den "else" -Abschnitt der Bedingung zu sehen. // mysteryObject = [[Schiffszuteilung] init]; if ([mysteryObject conformTToProtocol: @protocol (CoordinateSupport)]) NSLog (@ "OK, um Koordinatenunterstützung anzunehmen."); NSLog (@ "Das Objekt befindet sich unter (% 0.2f,% 0.2f,% 0.2f)", [mysteryObject x], [mysteryObject y], [mysteryObject z]); else NSLog (@ "Fehler: Es ist nicht sicher, eine Koordinatenunterstützung anzunehmen."); NSLog (@ "Ich habe keine Ahnung, wo das Objekt ist ..."); return 0;
Wenn Sie die Zeile auskommentieren, die das neu schreibt mysteryObject
zu einem Schiff
Zum Beispiel die conformsToProtocol:
Methode wird zurückkehren NEIN
, und Sie können die von nicht definierte API nicht sicher verwenden CoordinateSupport
. Wenn Sie nicht sicher sind, welche Art von Objekt eine Variable enthalten soll, ist diese Art der dynamischen Protokollprüfung wichtig, damit Ihr Programm nicht abstürzt, wenn Sie versuchen, eine Methode aufzurufen, die nicht vorhanden ist.
Beachten Sie auch das Neue @Protokoll()
Richtlinie. Das funktioniert sehr gerne @Wähler()
, Anstelle eines Methodennamens wird ein Protokollname verwendet. Es wird ein zurückgegeben Protokoll
Objekt, an das übergeben werden kann conformsToProtocol:
, unter anderen eingebauten Methoden. Die Protokoll-Header-Datei funktioniert nicht müssen importiert werden @Protokoll()
arbeiten.
Wenn Sie mit vielen Protokollen arbeiten, werden Sie schließlich in eine Situation geraten, in der zwei Protokolle aufeinander angewiesen sind. Diese zirkuläre Beziehung stellt ein Problem für den Compiler dar, da er keines der beiden ohne den anderen erfolgreich importieren kann. Nehmen wir zum Beispiel an, wir haben versucht, einige GPS-Funktionen in eine zu abstrahieren GPSSupport
Protokoll, möchten aber in der Lage sein, zwischen den "normalen" Koordinaten unseres bestehenden umzuwandeln CoordinateSupport
und die von GPSSupport
. Das GPSSupport
Protokoll ist ziemlich einfach:
#einführen#import "CoordinateSupport.h" @protocol GPSSupport - (ungültig) copyCoordinatesFromObject: (id )das Objekt; @Ende
Dies stellt keine Probleme dar, das heißt, bis wir auf das referenzieren müssen GPSSupport
Protokoll von CoordinateSupport.h
:
#einführen#import "GPSSupport.h" @protocol CoordinateSupport @ property double x; @ property double y; @ property double z; - (NSArray *) arrayFromPosition; - (doppelte) Größenordnung; - (doppelt) getDistanceFromObject: (id )das Objekt; - (void) copyGPSCoordinatesFromObject: (id )das Objekt; @Ende
Jetzt die CoordinateSupport.h
Datei erfordert die GPSSupport.h
Datei richtig kompilieren und umgekehrt. Es ist ein Hühnchen-oder-das-Ei-Problem, und der Compiler wird es nicht besonders mögen:
Das Auflösen der rekursiven Beziehung ist einfach. Alles, was Sie tun müssen, ist, eines der Protokolle weiterzuleiten, anstatt es direkt zu importieren:
#einführen@protocol CoordinateSupport; @protocol GPSSupport - (ungültig) copyCoordinatesFromObject: (id )das Objekt; @Ende
Alles @protocol CoordinateSupport;
sagt ist das CoordinateSupport
ist in der Tat ein Protokoll und der Compiler kann davon ausgehen, dass es existiert, ohne es zu importieren. Beachten Sie das Semikolon am Ende der Anweisung. Dies könnte in einem der beiden Protokolle erfolgen; Der Punkt ist, den Zirkelverweis zu entfernen. Dem Compiler ist es egal, wie Sie das machen.
Protokolle sind eine unglaublich leistungsfähige Funktion von Objective-C. Damit können Sie Beziehungen zwischen beliebigen Klassen erfassen, wenn es nicht möglich ist, sie mit einer gemeinsamen übergeordneten Klasse zu verbinden. Wir verwenden mehrere integrierte Protokolle in iOS Prägnant, So viele Kernfunktionen einer iPhone- oder iPad-App sind als Protokolle definiert.
Im nächsten Kapitel werden Ausnahmen und Fehler vorgestellt, zwei sehr wichtige Werkzeuge zur Verwaltung der Probleme, die beim Schreiben von Objective-C-Programmen zwangsläufig auftreten.
Diese Lektion stellt ein Kapitel von Objective-C Succinctly dar, ein kostenloses eBook des Teams von Syncfusion.