Protokollorientierte Programmierung in Swift 2

Einführung

Mit der Veröffentlichung von Swift 2 hat Apple die Programmiersprache Swift um eine Reihe neuer Funktionen und Funktionen erweitert. Eine der wichtigsten war jedoch eine Überarbeitung der Protokolle. Die mit den Swift-Protokollen verfügbare verbesserte Funktionalität ermöglicht eine neue, protokollorientierte Programmierung. Dies steht im Gegensatz zu dem allgemeineren objektorientierten Programmierstil, an den viele von uns gewöhnt sind.

In diesem Tutorial werde ich Ihnen die Grundlagen der protokollorientierten Programmierung in Swift zeigen und zeigen, wie sich diese von der objektorientierten Programmierung unterscheiden.

Voraussetzungen

Für dieses Lernprogramm ist es erforderlich, dass Sie Xcode 7 oder höher ausführen. Dies beinhaltet Unterstützung für Version 2 der Programmiersprache Swift.

1. Protokollgrundlagen

Wenn Sie mit Protokollen noch nicht vertraut sind, können Sie damit die Funktionalität einer vorhandenen Klasse oder Struktur erweitern. Ein Protokoll kann als Blaupause oder Schnittstelle betrachtet werden, die eine Reihe von Eigenschaften und Methoden definiert. Eine Klasse oder Struktur, die einem Protokoll entspricht, ist erforderlich, um diese Eigenschaften und Methoden mit Werten bzw. Implementierungen auszufüllen.

Es sollte auch beachtet werden, dass jede dieser Eigenschaften und Methoden als optional gekennzeichnet werden kann, was bedeutet, dass konforme Typen nicht erforderlich sind, um sie zu implementieren. Eine Protokolldefinition und Klassenkonformität in Swift könnte folgendermaßen aussehen:

Protokoll Welcome var welcomeMessage: String get set optional func welcome () class Welcomer: Willkommen var welcomeMessage = "Hallo Welt!" func welcome () print (welcomeMessage)

2. Ein Beispiel

Öffnen Sie zunächst Xcode und erstellen Sie einen neuen Spielplatz für iOS oder OS X. Nachdem Xcode den Spielplatz erstellt hat, ersetzen Sie den Inhalt durch Folgendes:

Protokoll Anfahrbar var topSpeed: Int get Protokoll Reversible var reverseSpeed: Int get Protokoll Transport var seatCount: Int get

Wir definieren drei Protokolle, die jeweils eine Eigenschaft enthalten. Als Nächstes erstellen wir eine Struktur, die diesen drei Protokollen entspricht. Fügen Sie dem Spielplatz den folgenden Code hinzu:

struct Auto: fahrbar, reversibel, transportfähig var topSpeed ​​= 150 var reverseSpeed ​​= 20 var seatCount = 5

Möglicherweise haben Sie bemerkt, dass wir, anstatt eine Klasse zu erstellen, die diesen Protokollen entspricht, eine Struktur erstellt haben. Wir vermeiden dies, um eines der typischen Probleme der objektorientierten Programmierung zu vermeiden, Objektreferenzen.

Stellen Sie sich zum Beispiel vor, Sie haben zwei Objekte, A und B. A erstellt einige Daten selbst und behält einen Verweis auf diese Daten. A teilt diese Daten dann durch Referenz mit B, was bedeutet, dass beide Objekte eine Referenz auf dasselbe Objekt haben. Ohne A zu wissen, ändert B die Daten auf irgendeine Weise.

Dies scheint zwar kein großes Problem zu sein, es kann jedoch vorkommen, dass A nicht mit der Änderung der Daten gerechnet hat. Objekt A kann Daten finden, mit denen er nicht umgehen kann. Dies ist ein häufiges Risiko für Objektreferenzen.

In Swift werden Strukturen vorbeigeführt Wert anstatt von Referenz. Dies bedeutet, dass im obigen Beispiel die Daten, die von A erstellt wurden, anstelle eines Objekts als Struktur gepackt und für B freigegeben wurden, kopiert und nicht als Referenz verwendet wurden. Dies würde dann dazu führen, dass sowohl A als auch B ihre eigene eindeutige Kopie desselben Datenelements haben. Eine von B vorgenommene Änderung hat keine Auswirkungen auf die von A verwaltete Kopie.

Zerbrechen der FahrbarReversibel, und Transport Komponenten in einzelne Protokolle ermöglichen auch eine größere Anpassung als traditionelle Klassenvererbung. Wenn Sie mein erstes Tutorial über das neue GameplayKit-Framework in iOS 9 gelesen haben, ist dieses protokollorientierte Modell der Entity- und Components-Struktur sehr ähnlich, die im GameplayKit-Framework verwendet wird.

Durch diesen Ansatz können benutzerdefinierte Datentypen die Funktionalität von mehreren Quellen und nicht von einer einzelnen Superklasse erben. Wenn wir wissen, was wir bisher haben, könnten wir die folgenden Klassen erstellen:

  • eine Klasse mit Komponenten des Fahrbar und Reversibel Protokolle
  • eine Klasse mit Komponenten des Fahrbar und Transportabel Protokolle
  • eine Klasse mit Komponenten des Reversibel und Transportabel Protokolle

Bei der objektorientierten Programmierung besteht der logischste Weg, diese drei Klassen zu erstellen, darin, von einer Superklasse zu erben, die die Komponenten aller drei Protokolle enthält. Dieser Ansatz führt jedoch dazu, dass die Superklasse komplizierter ist, als es sein muss, und dass jede Unterklasse mehr Funktionalität erbt, als sie benötigt.

3. Protokollerweiterungen

Alles, was ich Ihnen bisher gezeigt habe, ist seit seiner Veröffentlichung im Jahr 2014 in Swift möglich. Dieselben protokollorientierten Konzepte könnten sogar auf Objective-C-Protokolle angewendet werden. Aufgrund der Einschränkungen bei den Protokollen war eine echte protokollorientierte Programmierung jedoch erst möglich, nachdem der Swift-Sprache in Version 2 eine Reihe wichtiger Funktionen hinzugefügt wurde. Eine der wichtigsten Funktionen ist Protokollerweiterungen, einschließlich bedingte Erweiterungen.

Zuerst wollen wir das erweitern Fahrbar Protokoll und fügen Sie eine Funktion hinzu, um zu bestimmen, ob ein bestimmtes Fahrbar ist schneller als ein anderer. Fügen Sie Ihrem Spielplatz Folgendes hinzu:

extension Drivable func isFasterThan (item: Drivable) -> Bool return self.topSpeed> item.topSpeed let sedan = Auto () let sportsCar = Auto (topSpeed: 250, Rückwärtsgeschwindigkeit: 25, SeatCount: 2) sedan.isFasterThan (Sportwagen)

Sie sehen, wenn der Code des Spielplatzes ausgeführt wird, wird ein Wert von ausgegeben falschwie dein Limousine Auto hat einen Standardwert Höchstgeschwindigkeit von 150, das ist weniger als das Sportwagen.

Möglicherweise haben Sie festgestellt, dass wir eine Funktion bereitgestellt haben Definition anstatt einer Funktion Erklärung. Dies erscheint merkwürdig, da Protokolle nur Deklarationen enthalten sollen. Recht? Dies ist ein weiteres sehr wichtiges Merkmal von Protokollerweiterungen in Swift 2, Standardverhalten. Durch die Erweiterung eines Protokolls können Sie eine Standardimplementierung für Funktionen und berechnete Eigenschaften bereitstellen, so dass protokollkonforme Klassen nicht erforderlich sind.

Als nächstes werden wir eine andere definieren Fahrbar Protokollerweiterung, aber diesmal definieren wir sie nur für Werttypen, die ebenfalls dem entsprechen Reversibel Protokoll. Diese Erweiterung enthält dann eine Funktion, die bestimmt, welches Objekt den besseren Geschwindigkeitsbereich hat. Wir können dies mit dem folgenden Code erreichen:

Endung, in der selbst: reversible func hasLargerRangeThan (item: Self) -> Bool return (self.topSpeed ​​+ self.reverseSpeed)> (item.topSpeed ​​+ item.reverseSpeed) sportsCar.hasLargerRangeThan (Limousine)

Das Selbst Schlüsselwort, das mit einem "S" geschrieben wird, wird verwendet, um die Klasse oder Struktur darzustellen, die dem Protokoll entspricht. Im obigen Beispiel ist das Selbst Schlüsselwort steht für das Auto Struktur.

Nachdem der Code des Spielplatzes ausgeführt wurde, gibt Xcode die Ergebnisse in der rechten Seitenleiste aus (siehe Abbildung unten). Beachten Sie, dass Sportwagen hat eine größere Reichweite als Limousine.

4. Arbeiten mit der Swift Standard Library

Das Definieren und Erweitern eigener Protokolle kann zwar sehr nützlich sein, die wahre Stärke von Protokollerweiterungen zeigt sich jedoch beim Arbeiten mit der Swift-Standardbibliothek. Auf diese Weise können Sie Eigenschaften oder Funktionen zu vorhandenen Protokollen hinzufügen, z CollectionType (verwendet für Dinge wie Arrays und Wörterbücher) und Gleichwertig (Ermitteln können, wann zwei Objekte gleich sind oder nicht). Mit bedingten Protokollerweiterungen können Sie auch sehr spezifische Funktionen für einen bestimmten Objekttyp bereitstellen, der einem Protokoll entspricht.

Auf unserem Spielplatz erweitern wir die CollectionType Protokoll und erstellen Sie zwei Methoden, eine, um die durchschnittliche Höchstgeschwindigkeit von Autos in einem zu erhalten Auto Array und ein anderes für die durchschnittliche Rückwärtsgeschwindigkeit. Fügen Sie Ihrem Spielplatz den folgenden Code hinzu:

Erweiterung CollectionType Wobei Self.Generator.Element: Drivable func averageTopSpeed ​​() -> Int var total = 0, count = 0 für Element in self total + = item.topSpeed ​​count ++ return (total / count)(Elemente: T) -> Int var total = 0, count = 0 für Artikel in item total + = item.reverseSpeed ​​count ++ return (gesamt / count) let cars = [Auto (), Limousine, Sportwagen] .averageTopSpeed ​​() averageReverseSpeed ​​(Autos)

Die Protokollerweiterung, die die definiert averageTopSpeed Methode nutzt bedingte Erweiterungen in Swift 2. Im Gegensatz dazu averageReverseSpeed Eine Funktion, die wir direkt darunter definieren, ist ein weiterer Weg, um ein ähnliches Ergebnis unter Verwendung von Swift-Generics zu erzielen. Ich persönlich bevorzuge das sauberere Aussehen CollectionType Protokollerweiterung, aber es liegt an den persönlichen Vorlieben.

In beiden Funktionen durchlaufen wir das Array, summieren die Gesamtmenge und geben dann den Durchschnittswert zurück. Beachten Sie, dass die Anzahl der Elemente im Array manuell gezählt wird, wenn Sie mit arbeiten CollectionType eher als regelmäßig Array Artikel eingeben, die Anzahl Eigenschaft ist ein Self.Index.Distance Geben Sie den Wert statt eines ein Int.

Sobald Ihr Spielplatz den gesamten Code ausgeführt hat, sollten Sie eine durchschnittliche Höchstgeschwindigkeit der Ausgabe von sehen 183 und eine durchschnittliche Rückwärtsgeschwindigkeit von 21.

5. Wichtigkeit von Klassen

Obwohl die protokollorientierte Programmierung eine sehr effiziente und skalierbare Methode für die Verwaltung Ihres Codes in Swift darstellt, gibt es immer noch vollkommen gültige Gründe für die Verwendung von Klassen bei der Entwicklung in Swift:

Rückwärtskompatibilität

Die meisten der iOS-, watchOS- und tvOS-SDKs sind objektorientiert in Objective-C geschrieben. Wenn Sie mit einer der in diesen SDKs enthaltenen APIs interagieren müssen, müssen Sie die in diesen SDKs definierten Klassen verwenden.

Verweis auf eine externe Datei oder ein externes Element

Der Swift-Compiler optimiert die Lebensdauer von Objekten je nachdem, wann und wo sie verwendet werden. Aufgrund der Stabilität von klassenbasierten Objekten bleiben Ihre Verweise auf andere Dateien und Elemente konsistent.

Objektreferenzen

Objektreferenzen sind genau das, was Sie manchmal benötigen, wenn Sie beispielsweise Informationen in ein bestimmtes Objekt einbinden, z. B. in einen Grafik-Renderer. Die Verwendung von Klassen mit impliziter Freigabe ist in solchen Situationen wichtig, da Sie sicherstellen müssen, dass der Renderer, an den Sie die Daten senden, immer noch derselbe Renderer ist wie zuvor.

Fazit

Hoffentlich sehen Sie am Ende dieses Tutorials das Potenzial der protokollorientierten Programmierung in Swift und wie Sie damit Ihren Code rationalisieren und erweitern können. Diese neue Codierungsmethode wird zwar die objektorientierte Programmierung nicht vollständig ersetzen, bietet jedoch eine Reihe sehr nützlicher neuer Möglichkeiten.

Von Standardverhalten bis hin zu Protokollerweiterungen wird die protokollorientierte Programmierung in Swift von vielen zukünftigen APIs übernommen und die Art und Weise, wie wir über Softwareentwicklung nachdenken, völlig verändern.

Wie immer sollten Sie Ihre Kommentare und Rückmeldungen in den nachstehenden Kommentaren hinterlassen.