Objective-C Kurz gesagt Protokolle

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.


Protokoll erstellen

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 Protokolldateien

Wie ü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 benennen

Klicken 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

Annahme eines Protokolls

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:

Unvollständige Implementierungswarnung für Person

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 

Vorteile von Protokollen

Ohne Protokolle hätten wir zwei Möglichkeiten, um beides sicherzustellen Schiff und Person Diese gemeinsame API wurde implementiert:

  1. Deklarieren Sie dieselben Eigenschaften und Methoden in beiden Schnittstellen erneut.
  2. Definieren Sie die API in einer abstrakten Superklasse und definieren Sie sie 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 Protokolls

Aufgrund 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 als Pseudo-Typen

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.

Dynamische Konformitätsprüfung

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.


Vorwärtsdeklarierende Protokolle

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:

Compiler-Fehler, verursacht durch Zirkularprotokollreferenzen

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.


Zusammenfassung

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.