Schnell von Grund auf Verschlüsse

Wenn Sie mit Blöcken in C oder Objective-C oder Lambdas in Ruby gearbeitet haben, wird es Ihnen nicht schwer fallen, sich mit dem Konzept der Schließung zu beschäftigen. Closures sind nichts anderes als Funktionsblöcke, die Sie in Ihrem Code weitergeben können.

Tatsächlich haben wir bereits in den vorherigen Lektionen mit Schließungen gearbeitet. Das ist richtig: Funktionen sind auch Schließungen. Beginnen wir mit den Grundlagen und untersuchen Sie die Anatomie eines Verschlusses.

1. Was ist ein Abschluss??

Wie gesagt, ein Abschluss ist ein Funktionsblock, den Sie in Ihrem Code weitergeben können. Sie können einen Abschluss als Argument einer Funktion übergeben oder als Eigenschaft eines Objekts speichern. Verschlüsse haben viele Anwendungsfälle.

Der Name Schließung Hinweise auf eine der wichtigsten Eigenschaften von Verschlüssen. Ein Abschluss erfasst die Variablen und Konstanten des Kontexts, in dem er definiert ist. Dies wird manchmal als bezeichnet schließen über diese Variablen und Konstanten. Am Ende dieser Lektion werden wir uns eingehender mit der Erfassung von Werten befassen.

Flexibilität

Sie haben bereits gelernt, dass Funktionen unglaublich leistungsstark und flexibel sein können. Da es sich bei Funktionen um Verschlüsse handelt, sind Verschlüsse ebenso flexibel. In diesem Artikel erfahren Sie, wie flexibel und leistungsstark sie sind.

Speicherverwaltung

Die Programmiersprache C hat ein ähnliches Konzept, Blöcke. Schließungen in Swift haben jedoch einige Vorteile. Einer der Hauptvorteile von Closures in Swift ist, dass Sie als Entwickler sich über das Speichermanagement keine Gedanken machen müssen.

Sogar Rückhaltezyklen, die in C oder Objective-C nicht ungewöhnlich sind, werden von Swift durchgeführt. Dadurch werden schwer zu findende Speicherlecks oder Abstürze durch ungültige Zeiger reduziert.

2. Syntax

Die grundlegende Syntax eines Abschlusses ist nicht schwierig und kann Sie an globale und verschachtelte Funktionen erinnern, die wir zuvor in dieser Serie behandelt haben. Schauen Sie sich das folgende Beispiel an.

(a: Int) -> Int im Gegenzug a + 1

Das erste, was Sie bemerken, ist, dass der gesamte Verschluss in eine geschweifte Klammer gehüllt ist. Die Parameter des Abschlusses werden in ein Paar Klammern gehüllt, die durch die Zeichenfolge vom Rückgabetyp getrennt werden -> Symbol. Die vorstehende Schließung akzeptiert ein Argument, ein, vom Typ Int, und gibt ein zurück Int. Der Körper des Verschlusses beginnt nach dem im Stichwort.

Benannte Closures, also globale und verschachtelte Funktionen, sehen etwas anders aus. Das folgende Beispiel soll die Unterschiede verdeutlichen.

Funktionsschritt (_ a: Int) -> Int return a + 1

Die auffälligsten Unterschiede sind die Verwendung der func Schlüsselwort und die Position der Parameter und Rückgabetyp. Ein Verschluss beginnt und endet mit einer geschweiften Klammer, die die Parameter, den Rückgabetyp und den Verschlusskörper umschließt. Bedenken Sie trotz dieser Unterschiede, dass jede Funktion eine Schließung ist. Nicht jeder Verschluss ist jedoch eine Funktion.

3. Verschlüsse als Parameter

Verschlüsse sind mächtig, und das folgende Beispiel zeigt, wie nützlich sie sein können. Im Beispiel erstellen wir ein Array von Zuständen. Wir rufen das an Karte(_:) Funktion für das Array, um ein neues Array zu erstellen, das nur die ersten beiden Buchstaben jedes Bundesstaats als Großbuchstaben enthält.

var states = ["California", "New York", "Texas", "Alaska"] lassen abgekürzteState = states.map ((state: String) -> Zeichenfolge in let index = state.index (state.startIndex, offsetBy) : 2) return state.substring (to: index) .uppercased ()) print (abgekürzteStates)

Das Karte(_:) Funktion oder Methode ist in vielen Programmiersprachen und Bibliotheken wie Ruby, PHP und JavaScript üblich. Im obigen Beispiel ist das Karte(_:) Funktion wird auf der aufgerufen Zustände array, transformiert seinen Inhalt und gibt ein neues Array zurück, das die transformierten Werte enthält. Mach dir keine Sorgen über den Körper des Verschlusses.

Typ Inferenz

Zuvor haben wir in dieser Serie gelernt, dass Swift ziemlich schlau ist. Lass mich dir zeigen, wie schlau. Das Array von Zuständen ist ein Array von Strings. Weil wir das anrufen Karte(_:) Funktion auf dem Array, weiß Swift, dass die Zustand Argument ist vom Typ String. Dies bedeutet, dass wir den Typ weglassen können, wie im aktualisierten Beispiel unten gezeigt.

let abbreviatedStates = states.map ((state) -> String in let index = state.index (state.startIndex, offsetBy: 2) gibt state.substring (an: index) .uppercased () zurück.

Es gibt noch ein paar Dinge, die wir aus dem obigen Beispiel weglassen können, woraus sich der folgende Einzeiler ergibt.

let abbreviatedStates = states.map (state in state.substring (an: state.index (state.startIndex, offsetBy: 2)). uppercased ())

Lassen Sie mich erklären, was passiert. 

Der Compiler kann daraus schließen, dass wir eine Zeichenfolge aus dem Abschluss zurückgeben, den wir an den übergeben Karte(_:) Funktion, was bedeutet, dass es keinen Grund gibt, sie in die Definition des Abschlussausdrucks aufzunehmen. 

Wir können dies nur tun, wenn der Abschlußkörper eine einzige Aussage enthält. In diesem Fall können wir diese Aussage in dieselbe Zeile wie die Definition des Abschlusses stellen, wie im obigen Beispiel gezeigt. Weil es keinen Rückgabetyp in der Definition gibt und nein -> Symbol vor dem Rückgabetyp können wir die Klammern, die die Parameter des Abschlusses einschließen, weglassen.

Abkürzungsargumentnamen

Es hört hier jedoch nicht auf. Wir können Kurznamen verwenden, um den obigen Abschlussausdruck noch weiter zu vereinfachen. Wenn Sie einen Inline-Abschlussausdruck wie im obigen Beispiel verwenden, können Sie die Liste der Parameter einschließlich der Parameter weglassen im Schlüsselwort, das die Parameter vom Schließkörper trennt.

Im Abschlusskörper verweisen wir auf die Argumente mit Kurznamen, die Swift uns zur Verfügung stellt. Das erste Argument wird von referenziert $ 0, der zweite vorbei 1 US-Dollar, usw.

In dem aktualisierten Beispiel habe ich die Liste der Parameter und die im Schlüsselwort und ersetzt das Zustand Argument im Schließungskörper mit dem Kurznamen Argumentnamen $ 0. Die resultierende Aussage ist übersichtlicher, ohne die Lesbarkeit zu beeinträchtigen.

let abgekürzteStaaten = states.map ($ 0substring (bis: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ()

Schleppende Verschlüsse

Die Programmiersprache Swift definiert auch ein Konzept, das als nachlaufende Schließungen bezeichnet wird. Die Idee ist einfach. Wenn Sie eine Schließung als letztes Argument einer Funktion übergeben, können Sie diese Schließung außerhalb der Klammern des Funktionsaufrufs platzieren. Das folgende Beispiel zeigt, wie das funktioniert.

let abbreviatedStates = states.map () $ 0substring (bis: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ()

Wenn das einzige Argument des Funktionsaufrufs die Schließung ist, können die Klammern des Funktionsaufrufs sogar weggelassen werden.

let abgekürzteStaaten = states.map $ 0substring (bis: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ()

Beachten Sie, dass dies auch für Abschlüsse funktioniert, die mehrere Anweisungen enthalten. Dies ist in der Tat der Hauptgrund dafür, dass nachgestellte Verschlüsse in Swift verfügbar sind. Wenn ein Abschluss lang oder komplex ist und das letzte Argument einer Funktion ist, ist es häufig besser, die nachfolgende Abschlusssyntax zu verwenden.

let abbreviatedStates = states.map (state) -> String in let index = state.index (state.startIndex, offsetBy: 2) gibt state.substring (an: index) .uppercased () zurück.

4. Werte erfassen

Bei der Verwendung von Schließungen verwenden oder manipulieren Sie häufig Konstanten und Variablen aus dem umgebenden Kontext der Schließung im Schließkörper. Dies wird häufig als Werterfassung bezeichnet. Es bedeutet einfach, dass ein Abschluss die Werte von Konstanten und Variablen aus dem Kontext erfassen kann, in dem er definiert ist. Nehmen Sie das folgende Beispiel, um das Konzept der Wertschöpfung besser zu verstehen.

func changeCase (Großbuchstaben: Bool, ofStrings-Strings: String…) -> [String] var newStrings = [String] () func changeToUppercase () für s in Strings newStrings.append (s.uppercased ()) func changeToLowerCase () für s in Strings newStrings.append (s.lowercased ()), wenn Großbuchstaben changeToUppercase () else changeToLowerCase () newStrings zurückgeben lassen "," New York ") let lowercasedStates = changeCase (Großbuchstaben: false, ofStrings:" California "," New York ")

Ich bin sicher, Sie stimmen zu, dass das obige Beispiel ein wenig durchdacht ist, aber es zeigt deutlich, wie die Wertschöpfung in Swift funktioniert. Die verschachtelten Funktionen, changeToUppercase () und changeToLowercase (), Zugriff auf die Argumente der äußeren Funktion haben, Zustände, ebenso wie newStates in der äußeren Funktion deklarierte Variable. 

Lass mich erklären, was passiert.

Das changeCase (Großbuchstaben: ofStrings :) Die Funktion akzeptiert einen Boolean als erstes Argument und einen variadischen Parameter des Typs String als zweiten Parameter. Die Funktion gibt ein Array von Strings zurück, das aus den an die Funktion übergebenen Strings als zweites Argument besteht. Im Rumpf der Funktion erstellen wir ein veränderbares String-Array, neueStrings, in dem wir die modifizierten Zeichenketten speichern.

Die verschachtelten Funktionen durchlaufen die Zeichenfolgen, die an die übergeben werden changeCase (Großbuchstaben: ofStrings :) Funktion und ändern Sie den Fall jeder Zeichenfolge. Wie Sie sehen, haben sie direkten Zugriff auf die an die übergebenen Zeichenketten changeCase (Großbuchstaben: ofStrings :) Funktion sowie die neueStrings Array, das im Hauptteil von deklariert ist changeCase (Großbuchstaben: ofStrings :) Funktion.

Wir prüfen den Wert von Großbuchstaben, rufen Sie die entsprechende Funktion auf und geben Sie das zurück neueStrings Array. Die beiden Zeilen am Ende des Beispiels zeigen, wie der changeCase (Großbuchstaben: ofStrings :) Funktion funktioniert.

Auch wenn ich mit Funktionen das Erfassen von Werten demonstriert habe, denken Sie daran, dass jede Funktion eine Schließung ist. Mit anderen Worten gelten für nicht benannte Schließungen die gleichen Regeln.

Verschlüsse

In diesem Artikel wurde mehrfach erwähnt: Funktionen sind Schließungen. Es gibt drei Arten von Verschlüssen:

  • globale Funktionen
  • verschachtelte Funktionen
  • Schließungsausdrücke

Globale Funktionen wie die print (_: Trennzeichen: Terminator :) Funktion der Swift-Standardbibliothek, keine Werte erfassen. Geschachtelte Funktionen haben jedoch Zugriff auf und können die Werte von Konstanten und Werten der Funktionen erfassen, in denen sie definiert sind. Das vorige Beispiel veranschaulicht dieses Konzept.

Schließungsausdrücke, auch als unbenannte Schließungen bezeichnet, können die Werte von Konstanten und Variablen des Kontexts erfassen, in dem sie definiert sind. Dies ist sehr ähnlich zu verschachtelten Funktionen.

Kopieren und Referenzieren

Ein Abschluss, der den Wert einer Variablen erfasst, kann den Wert dieser Variablen ändern. Swift ist klug genug, um zu wissen, ob es die Werte der erfassten Konstanten und Variablen kopieren oder referenzieren soll.

Entwickler, die Swift lernen und wenig Erfahrung mit anderen Programmiersprachen haben, werden dieses Verhalten als selbstverständlich betrachten. Es ist jedoch ein wichtiger Vorteil, dass Swift versteht, wie die erfassten Werte in einer Schließung verwendet werden, und somit das Speichermanagement für uns übernehmen kann.

Fazit

Verschlüsse sind ein wichtiges Konzept und werden in Swift häufig verwendet. Mit ihnen können Sie flexiblen, dynamischen Code schreiben, der leicht zu schreiben und zu verstehen ist. 

Im nächsten Artikel untersuchen wir die objektorientierte Programmierung in Swift, beginnend mit Objekten, Strukturen und Klassen.

Wenn Sie wissen möchten, wie Sie mit Swift 3 erweiterte Funktionen für reale Apps codieren können, lesen Sie unseren Kurs "Weiter mit Swift: Animationen, Netzwerke und benutzerdefinierte Steuerelemente". Folgen Sie Markus Mühlberger, während Sie eine funktionale iOS-Wetter-App mit Live-Wetterdaten, benutzerdefinierten UI-Komponenten und ein paar Animationen programmieren, die alles zum Leben erwecken.