Zum Inhalt springen
Swift Anfรคnger 30 min

Funktionen & Closures

Funktionen mit Argument-Labels, Default-Werten, Variadic-Parameter und Closures - die Grundlage von vielem in Swift, inklusive SwiftUI.

Aktualisiert:
Inhaltsverzeichnis

Funktionen & Closures

Swift hat viele nette Details bei Funktionen, die du in anderen Sprachen so nicht findest. Und Closures sind der Schluessel zu vielen modernen Swift-APIs - besonders SwiftUI.

Grundform

func gruessen(name: String) {
    print("Hallo, \(name)!")
}

gruessen(name: "Anna")

Auffaellig: Beim Aufruf gibst du den Argument-Namen an. Das macht Swift-Code sehr lesbar.

Rueckgabewert

func addieren(a: Int, b: Int) -> Int {
    return a + b
}

let summe = addieren(a: 3, b: 4) // 7

Bei einzeiligen Funktionen kannst du return weglassen:

func addieren(a: Int, b: Int) -> Int {
    a + b
}

Argument-Labels

Swift unterscheidet zwischen Label und Parameter-Name:

func gruessen(person name: String) {
    print("Hallo, \(name)!")
}

gruessen(person: "Anna")
  • person ist das Label (beim Aufruf sichtbar)
  • name ist der Parameter-Name (innen genutzt)

Label unterdruecken

Mit _:

func addieren(_ a: Int, _ b: Int) -> Int {
    a + b
}

addieren(3, 4) // kein Label noetig

Warum Labels?

Labels machen den Aufruf selbstdokumentierend:

move(from: .home, to: .work)
calculate(cost: 100, withDiscount: 0.1)
send(message: "Hallo", to: "Anna")

Default-Werte

func gruessen(name: String = "Welt", gruss: String = "Hallo") {
    print("\(gruss), \(name)!")
}

gruessen()                              // "Hallo, Welt!"
gruessen(name: "Anna")                  // "Hallo, Anna!"
gruessen(gruss: "Guten Morgen", name: "Anna")

Variadic Parameter

Beliebig viele Argumente mit ...:

func summe(_ zahlen: Int...) -> Int {
    zahlen.reduce(0, +)
}

summe(1, 2, 3)           // 6
summe(10, 20, 30, 40)    // 100

Mehrere Rueckgabewerte mit Tupeln

func minMax(zahlen: [Int]) -> (min: Int, max: Int)? {
    guard let erste = zahlen.first else { return nil }

    var min = erste
    var max = erste
    for z in zahlen {
        if z < min { min = z }
        if z > max { max = z }
    }
    return (min, max)
}

if let (min, max) = minMax(zahlen: [3, 1, 4, 1, 5, 9, 2]) {
    print("min: \(min), max: \(max)")
}

Benannte Tuple-Elemente machen den Aufruf noch klarer.

inout Parameter

Wenn die Funktion einen uebergebenen Wert aendern soll:

func verdoppeln(_ zahl: inout Int) {
    zahl *= 2
}

var x = 5
verdoppeln(&x)  // & beim Aufruf
print(x) // 10

Selten genutzt - meistens bessere Rueckgabewert-Loesung.

Funktionen als Werte

Funktionen sind First-Class Citizens:

func verdoppeln(_ n: Int) -> Int { n * 2 }

let f: (Int) -> Int = verdoppeln
print(f(5)) // 10

An andere Funktionen uebergeben:

func wende(_ f: (Int) -> Int, auf zahl: Int) -> Int {
    f(zahl)
}

print(wende(verdoppeln, auf: 5)) // 10

Closures

Eine Closure ist eine anonyme Funktion:

let verdoppeln = { (n: Int) -> Int in
    n * 2
}

print(verdoppeln(5)) // 10

Der Aufbau: { (parameter) -> Rueckgabe in anweisungen }.

Kuerzere Syntax

Swift leitet viel her:

let zahlen = [1, 2, 3, 4, 5]

// Vollstaendig
let verdoppelt = zahlen.map({ (n: Int) -> Int in return n * 2 })

// Typ-Inferenz
let v2 = zahlen.map({ n in return n * 2 })

// Ohne return bei Einzeilern
let v3 = zahlen.map({ n in n * 2 })

// Shorthand-Argumente
let v4 = zahlen.map({ $0 * 2 })

// Trailing Closure - am gelaeufigsten
let v5 = zahlen.map { $0 * 2 }

Alle fuenf sind gleichwertig - im Alltag nutzt du meist Form 4 oder 5.

Trailing Closure

Wenn die letzte Parameter einer Funktion eine Closure ist:

zahlen.forEach { zahl in
    print(zahl)
}

zahlen.filter { $0 > 2 }.map { $0 * 10 }

Das ist das Muster, das SwiftUI-Code so lesbar macht:

Button("Speichern") {
    speichereDaten()
}

Die { speichereDaten() } ist eine trailing Closure - kein magischer Syntax.

$0, $1, $2

Statt in kannst du direkt auf Argumente zugreifen:

zahlen.sorted { $0 < $1 }
zahlen.reduce(0) { $0 + $1 }

Higher-Order Collection Methods

let zahlen = [1, 2, 3, 4, 5]

zahlen.map { $0 * 2 }                    // [2, 4, 6, 8, 10]
zahlen.filter { $0 % 2 == 0 }           // [2, 4]
zahlen.reduce(0, +)                      // 15
zahlen.sorted()                          // [1, 2, 3, 4, 5]
zahlen.sorted { $0 > $1 }               // [5, 4, 3, 2, 1]
zahlen.contains { $0 > 3 }              // true
zahlen.first { $0 > 3 }                 // Optional(4)

Ketten bilden natuerliche Pipelines:

let ergebnis = zahlen
    .filter { $0 > 1 }
    .map { $0 * 10 }
    .reduce(0, +)
print(ergebnis) // 140

Capturing

Closures โ€œfangenโ€ Variablen aus ihrem Scope ein:

func makeZaehler() -> () -> Int {
    var count = 0
    return {
        count += 1
        return count
    }
}

let weiter = makeZaehler()
print(weiter()) // 1
print(weiter()) // 2
print(weiter()) // 3

Jeder Aufruf von makeZaehler() erzeugt einen eigenen count.

Zusammenfassung

  • Argument-Labels und Parameter-Namen sind getrennt (person name: String)
  • Default-Werte und Variadic Parameters (...) sind eingebaut
  • Closures sind anonyme Funktionen mit sehr kompakter Syntax
  • Trailing Closure ist der Schluessel zu idiomatischem Swift
  • Higher-Order Methods (map, filter, reduce) machen Datenverarbeitung elegant

Im naechsten Kapitel: Structs, Classes und Enums - die drei Wege, eigene Typen zu definieren.

Zurรผck zum Swift Kurs