Advertisement
  1. Code
  2. Mobile Development
  3. iOS Development

Swift from Scratch: Funktionsparameter, Typen und Verschachtelung

Scroll to top
Read Time: 11 min
This post is part of a series called Swift From Scratch.
Swift From Scratch: An Introduction to Functions
Swift From Scratch: Closures

() translation by (you can also view the original English article)

Im vorherigen Artikel haben wir die Grundlagen von Funktionen in Swift untersucht. Funktionen haben jedoch viel mehr zu bieten. In diesem Artikel werden wir die Untersuchung von Funktionen fortsetzen und Funktionsparameter, Verschachtelung und Typen untersuchen.

1. Lokale und externe Parameternamen

Lokale Parameternamen

Kommen wir zu einem der Beispiele des vorherigen Artikels zurück. Die printMessage-Funktion definiert einen Parameter, message.

1
func printMessage(message: String) {
2
    println(message)
3
}

Obwohl wir dem Parameter einen Namen geben, message wir den Namen nicht, wenn wir die Funktion aufrufen. Wir übergeben den Wert für den message parameter.

1
printMessage("Hello, world!")

Der Name, den wir in der Definition der Funktion definieren, ist ein lokaler Parametername. Der Wert des Parameters kann nur mit diesem Namen im Rumpf der Funktion referenziert werden. Funktionsparameter sind jedoch etwas flexibler. Lass mich erklären, was ich damit meine.

Objective-C ist für seine langen Methodennamen bekannt. Während dies für Außenstehende klobig und unelegant aussehen mag, macht es Methoden leicht verständlich und, wenn sie gut gewählt sind, sehr anschaulich. Wenn Sie glauben, dass Sie diesen Vorteil verloren haben, als Sie zu Swift gewechselt sind, dann werden Sie überrascht sein.

Externe Parameternamen

Wenn eine Funktion mehrere Parameter akzeptiert, ist nicht immer ersichtlich, welches Argument welchem Parameter entspricht. Sehen Sie sich das folgende Beispiel an, um das Problem besser zu verstehen.

1
func power(a: Int, b: Int) -> Int {
2
    var result = a
3
    
4
    for _ in 1..<b {
5
        result = result * a
6
    }
7
    
8
    return result
9
}

Die power funktion erhöht den Wert von a um den Exponenten b. Beide Parameter sind vom Typ Int. Während die meisten Benutzer intuitiv den Basiswert als erstes Argument und den Exponenten als zweites Argument übergeben, ist dies nicht klar aus dem Typ, dem Namen oder der Signatur der Funktion. Wie wir im vorherigen Artikel gesehen haben, ist das Aufrufen der Funktion unkompliziert.

1
power(2, 3)

Um Verwirrung zu vermeiden, können wir den Parametern einer Funktion externe Namen geben. Wir können dann diese externen Namen verwenden, wenn die Funktion aufgerufen wird, um eindeutig anzugeben, welches Argument welchem Parameter entspricht. Sehen Sie sich das aktualisierte Beispiel unten an.

1
func power(base a: Int, exponent b: Int) -> Int {
2
    var result = a
3
    
4
    for _ in 1..<b {
5
        result = result * a
6
    }
7
    
8
    return result
9
}

Beachten Sie, dass sich der Body der Funktion nicht geändert hat, da sich die lokalen Namen nicht geändert haben. Wenn wir jedoch die aktualisierte Funktion aufrufen, ist der Unterschied deutlich und das Ergebnis viel weniger verwirrend.

1
power(base: 2, exponent: 3)

Während die Typen beider Funktionen gleich sind (Int, Int) -> Int, sind die Funktionen unterschiedlich. Mit anderen Worten, die zweite Funktion ist keine Neudeklaration der ersten Funktion. Die Syntax zum Aufrufen der zweiten Funktion erinnert einige von Ihnen an Objective-C. Die Argumente sind nicht nur klar beschrieben, die Kombination von Funktions- und Parameternamen beschreibt den Zweck der Funktion.

In einigen Fällen möchten Sie die gleichen Namen für lokale und externe Parameternamen verwenden. Dies ist möglich und es ist nicht notwendig, den Parameternamen zweimal einzugeben. Im folgenden Beispiel verwenden wir die base und den exponent als lokale und externe Parameternamen.

1
func power(#base: Int, #exponent: Int) -> Int {
2
    var result = base
3
    
4
    for _ in 1..<exponent {
5
        result = result * base
6
    }
7
    
8
    return result
9
}

Wenn Sie dem Parameternamen ein # -Zeichen voranstellen, dient der Parametername als lokaler und externer Name des Parameters. Dies bedeutet auch, dass wir den Hauptteil der Funktion aktualisieren müssen.

Beachten Sie, dass Sie bei Verwendung eines externen Namens für einen Parameter diesen Namen beim Aufrufen der Funktion verwenden müssen. Dies bringt uns zu Standardwerten.

Standardwerte

Wir haben im vorherigen Artikel die Standardparameterwerte behandelt, aber es gibt ein wichtiges Standardverhalten, das Sie beachten müssen. Wenn Sie einen Standardwert für einen Parameter definieren, weist Swift dem Parameter automatisch einen externen Parameternamen zu. Schauen wir uns ein Beispiel des vorherigen Artikels an.

1
func printDate(date: NSDate, format: String = "YY/MM/dd") -> String {
2
    let dateFormatter = NSDateFormatter()
3
    dateFormatter.dateFormat = format
4
    return dateFormatter.stringFromDate(date)
5
}

Da der zweite Parameter, format, einen Standardwert hat, legt Swift automatisch den format des externen Parameters des format fest. Mit anderen Worten, das Ergebnis ist das gleiche wie wenn wir ein # -Zeichen mit einem Präfix format würden. Sie können dies testen, indem Sie die obige Funktion in Ihrem Spielplatz aufrufen. Was passiert, wenn Sie das Format übergeben, ohne den externen Parameternamen des zweiten Parameters zu verwenden? Die Antwort ist unten gezeigt.

Swift ist ziemlich klar, was wir tun sollen. Wenn Sie einen Parameter als optional definieren, setzt Swift automatisch den Namen des externen Parameters auf den lokalen Parameternamen. Die Idee hinter diesem Verhalten besteht darin, Verwirrung und Mehrdeutigkeit zu vermeiden.

Obwohl dieses Verhalten eine gute Best Practice ist, ist es möglich, es zu deaktivieren. In der aktualisierten Funktionsdefinition fügen wir einen Unterstrich hinzu, in dem wir normalerweise den Namen des externen Parameters hinzufügen würden. Dies sagt Swift, dass wir keinen externen Parameternamen für diesen bestimmten Parameter einstellen wollen, obwohl er einen Standardwert hat.

1
func formatDate(date: NSDate, _ format: String = "YY/MM/dd") -> String {
2
    let dateFormatter = NSDateFormatter()
3
    dateFormatter.dateFormat = format
4
    return dateFormatter.stringFromDate(date)
5
}

Wir können jetzt die Funktion formatDate aufrufen, ohne einen Namen für das zweite Argument anzugeben.

1
formatDate(NSDate(), "dd/MM/YY")

2. Parameter & Veränderlichkeit

Lassen Sie uns das erste Beispiel dieses Tutorials, die printMessage-Funktion, noch einmal betrachten. Was passiert, wenn wir den Wert des message parameters im Körper der Funktion ändern?

1
func printMessage(message: String) {
2
    message = "Print: \(message)"
3
    println(message)
4
}

Es dauert nicht lange, bis Swift anfängt, sich zu beschweren.

Standardmäßig sind die Parameter einer Funktion Konstanten. Mit anderen Worten, während wir auf die Werte von Funktionsparametern zugreifen können, können wir deren Wert nicht ändern. Um dieses Standardverhalten zu ändern, fügen Sie dem Parameternamen in der Funktionsdefinition das Schlüsselwort var hinzu. Swift erstellt dann eine variable Kopie des Wertes des Parameters, mit dem Sie im Körper der Funktion arbeiten können.

1
func printMessage(var message: String) {
2
    message = "Print: \(message)"
3
    println(message)
4
}

Beachten Sie, dass dies nicht bedeutet, dass Sie einen Wert übergeben, ihn in der Funktion ändern und den geänderten Wert verwenden können, nachdem die Funktion ihre Arbeit erledigt hat. Swift erstellt eine Kopie des Werts des Parameters, der nur für die Lebensdauer des Funktionsaufrufs existiert. Dies wird auch im folgenden Codeblock veranschaulicht, in dem wir eine Konstante an die Funktion printMessage übergeben.

1
func printMessage(var message: String) {
2
    message = "Print: \(message)"
3
    println(message)
4
}
5
6
let myMessage = "test"
7
8
printMessage(myMessage)

3. Variadische Parameter

Während der Begriff zunächst seltsam klingen mag, sind variadische Parameter bei der Programmierung üblich. Ein variadischer Parameter ist ein Parameter, der null oder mehr Werte akzeptiert. Die Werte müssen vom selben Typ sein. Die Verwendung variadischer Parameter in Swift ist trivial, wie das folgende Beispiel zeigt.

1
func sum(args: Int...) -> Int {
2
    var result = 0
3
    
4
    for a in args {
5
        result += a
6
    }
7
    
8
    return result
9
}

Die Syntax ist einfach zu verstehen. Um einen Parameter als variadisch zu markieren, hängen Sie drei Punkte an den Typ des Parameters an. Im Funktionskörper ist der Variadic-Parameter als Array zugänglich. Im obigen Beispiel ist args ein Array von Int-Werten.

Da Swift wissen muss, welche Argumente welchen Parametern entsprechen, muss ein variadischer Parameter der letzte Parameter sein. Es bedeutet auch, dass eine Funktion maximal einen variadischen Parameter haben kann.

Das Obige gilt auch, wenn eine Funktion Parameter mit Standardwerten hat. Der Variadic-Parameter sollte immer der letzte Parameter sein.

4. In-Out-Parameter

In diesem Lernprogramm haben Sie zuvor gelernt, wie Sie die Veränderlichkeit eines Parameters mithilfe des Schlüsselworts var definieren können. In diesem Abschnitt habe ich betont, dass der Wert eines Variablenparameters nur innerhalb des Funktionskörpers zugänglich ist. Wenn Sie einen Wert an eine Funktion übergeben, in der Funktion ändern und aus der Funktion zurückgeben möchten, verwenden Sie die Parameter "In-Out".

Das folgende Beispiel zeigt ein Beispiel dafür, wie In-Out-Parameter in Swift funktionieren und wie die Syntax aussieht.

1
func prependString(inout a: String, withString b: String) {
2
    a = b + a
3
}

Wir haben den ersten Parameter als In-Out-Parameter definiert, indem wir das Schlüsselwort input hinzufügen. Der zweite Parameter ist ein regulärer Parameter mit einem externen Namen von withString und einem lokalen Namen von b. Mal sehen, wie wir diese Funktion aufrufen.

1
var world = "world"
2
3
prependString(&world, withString: "Hello, ")

Wir deklarieren eine Variable, world, vom Typ String und übergeben sie an die Funktion perpendString. Der zweite Parameter ist ein String-Literal. Durch den Aufruf der Funktion wird der world der Weltvariablen Hello, world. Beachten Sie, dass dem ersten Argument ein Und-Zeichen, & vorangestellt ist, um anzuzeigen, dass es sich um einen In-Out-Parameter handelt.

Es versteht sich von selbst, dass Konstanten und Literale nicht als In-Out-Parameter übergeben werden können. Swift wird einen Fehler ausgeben, wenn Sie das tun, wie im folgenden Screenshot gezeigt.

Es ist offensichtlich, dass In-Out-Parameter keine Standardwerte haben, variadic sein oder als var oder let definiert sein können. Wenn Sie diese Details vergessen, wird Swift Sie mit einem Fehler erinnern.

5. Verschachteln

In C und Objective-C können Funktionen und Methoden nicht verschachtelt werden. In Swift sind verschachtelte Funktionen jedoch recht häufig. Die Funktionen, die wir in diesem und im vorherigen Artikel gesehen haben, sind Beispiele für globale Funktionen, die im globalen Geltungsbereich definiert sind.

Wenn wir eine Funktion innerhalb einer globalen Funktion definieren, beziehen wir uns auf diese Funktion als verschachtelte Funktion. Eine verschachtelte Funktion hat Zugriff auf die in ihrer umschließenden Funktion definierten Werte. Sehen Sie sich das folgende Beispiel an, um dies besser zu verstehen.

1
func printMessage(message: String) {
2
    let a = "hello world"
3
    
4
    func printHelloWorld() {
5
        println(a)
6
    }
7
}

Während die Funktionen in diesem Beispiel nicht besonders nützlich sind, veranschaulicht das Beispiel die Idee der geschachtelten Funktion und das Erfassen von Werten. Auf die Funktion printHelloWorld kann nur innerhalb der Funktion printMessage zugegriffen werden. Wie im Beispiel gezeigt, hat die Funktion printHelloWorld Zugriff auf die Konstante a. Der Wert wird von der verschachtelten Funktion erfasst und ist daher von dieser Funktion aus zugänglich. Swift kümmert sich um die Erfassung von Werten, einschließlich der Verwaltung der Erinnerung an diese Werte.

6. Funktionstypen

Im vorherigen Artikel haben wir kurz auf Funktionstypen eingegangen. Eine Funktion hat einen bestimmten Typ, der sich aus den Parametertypen der Funktion und ihrem Rückgabetyp zusammensetzt. Die printMessage-Funktion hat beispielsweise den Typ (String) -> (). Denken Sie daran, dass () Void symbolisiert, was einem leeren Tupel entspricht.

Da jede Funktion einen Typ hat, ist es möglich, eine Funktion zu definieren, die eine andere Funktion als Parameter akzeptiert. Das folgende Beispiel zeigt, wie dies funktioniert.

1
func printMessageWithFunction(message: String, printFunction: (String) -> ()) {
2
    printFunction(message)
3
}
4
5
let myMessage = "Hello, world!"
6
7
printMessageWithFunction(myMessage, printMessage)

Die Funktion printMessageWithFunction akzeptiert eine Zeichenfolge als ersten Parameter und eine Funktion des Typs (String) -> () als zweiten Parameter. Im Body der Funktion wird die übergebene Funktion mit dem message argument aufgerufen.

Das Beispiel veranschaulicht außerdem, wie die Funktion printMessageWithFunction aufgerufen werden kann. Die myMessage-Konstante wird als erstes Argument und die printMessage-Funktion, die wir zuvor definiert haben, als zweites Argument übergeben. Wie cool ist das?

Wie ich bereits erwähnt habe, ist es auch möglich, eine Funktion von einer Funktion zurückzugeben. Das nächste Beispiel ist ein bisschen erfunden, aber es zeigt, wie die Syntax aussieht.

1
func compute(addition: Bool) -> (Int, Int) -> Int {
2
    func add(a: Int, b: Int) -> Int {
3
        return a + b
4
    }
5
    
6
    func subtract(a: Int, b: Int) -> Int {
7
        return a - b
8
    }
9
    
10
    if addition {
11
        return add
12
    } else {
13
        return subtract
14
    }
15
}
16
17
let computeFunction = compute(true)
18
let result = computeFunction(1, 2)
19
println(result)

Die Compute-Funktion akzeptiert einen booleschen Wert und gibt eine Funktion vom Typ (Int, Int) -> Int zurück. Die Compute-Funktion enthält zwei verschachtelte Funktionen, die ebenfalls vom Typ sind (Int, Int) -> Int, add und subtract.

Die compute funktion gibt basierend auf dem Wert des add einen Verweis auf die addition- oder subtract funktion zurück. Das Beispiel zeigt auch, wie die compute funktion verwendet wird. Wir speichern einen Verweis auf die Funktion, die von der compute-Funktion in der computeFunction-Konstante zurückgegeben wird. Wir rufen dann die in computeFunction gespeicherte Funktion auf, übergeben 1 und 2, speichern das Ergebnis in result und drucken den Wert von result in der Standardausgabe. Das Beispiel mag komplex aussehen, aber es ist einfach zu verstehen, wenn Sie wissen, was vor sich geht.

Fazit

Sie sollten jetzt ein gutes Verständnis davon haben, wie Funktionen in Swift funktionieren und was Sie damit tun können. Funktionen sind ein häufiges Thema in Swift und Sie werden sie ausgiebig verwenden, wenn Sie mit Swift arbeiten.

Im nächsten Artikel tauchen wir zuerst in Schließungen ein, eine mächtige Konstruktion, die sehr an Blöcke in C und Objective-C, Verschlüsse in JavaScript und Lambdas in Ruby erinnert.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.