defer ist eine Kontrollflusskonstruktion in Swift, die die Ausführung eines Codeblocks für den Zeitpunkt des Verlassens des aktuellen Gültigkeitsbereichs (Scope) plant. Der defer-Block wird unabhängig davon ausgeführt, wie der Scope endet — return, break, throw, fatalError oder normale Beendigung. Laut dem Swift Language Guide (2025) werden mehrere defers in einem Scope in umgekehrter Reihenfolge ihrer Deklaration ausgeführt — der zuletzt deklarierte wird zuerst ausgeführt (LIFO). Dies macht defer unverzichtbar für die garantierte Ressourcenbereinigung: Schließen von Dateideskriptoren, Freigeben von Sperren, Freigeben temporärer Zeiger, ohne das Risiko, die Bereinigung bei vorzeitigem Verlassen zu verpassen.
Wichtigste Punkte
defer ist eine Kontrollflusskonstruktion in Swift, eingeführt in Swift 2.0 (2015), die die Ausführung ihres Blocks verzögert, bis der aktuelle Scope beendet ist. Das Hauptmerkmal: defer garantiert, dass sein Rumpf unabhängig davon ausgeführt wird, wie der Scope endet — erfolgreich (return), mit einem Fehler (throw), vorzeitig (break, continue) oder fatal (fatalError, precondition).
Syntaktisch sieht defer wie defer { /* Code */ } aus und kann überall innerhalb eines Scopes platziert werden. Der Swift-Compiler garantiert, dass der Code innerhalb von defer ausgeführt wird, selbst wenn zwischen der defer-Deklaration und dem Ende des Scopes eine Ausnahme oder ein return auftritt. Dies unterscheidet defer grundlegend von normalem Code am Ende einer Funktion, der bei vorzeitigem Verlassen übersprungen werden kann.
Laut einem Artikel von Chris Lattner (Swift-Erfinder, 2015) wurde defer von ähnlichen Konstrukten in anderen Sprachen inspiriert — defer in Go, finally in Java/Python, scope guard in C++ — jedoch mit einem wichtigen Unterschied: In Swift wird defer am Ende des Scopes ausgeführt, nicht unmittelbar nach einem try-catch-Block. Dies bietet ein vorhersagbareres Verhalten für die Bereinigung in Funktionen mit mehreren Ausstiegspunkten.
Verwenden Sie defer für symmetrisches Ressourcenmanagement: Datei öffnen → defer { close }, Sperre erwerben → defer { unlock }. Dieses Muster garantiert, dass die Ressourcenfreigabe unter keinen Umständen vergessen wird.
Wenn mehrere defers im selben Scope deklariert werden, werden sie in umgekehrter Reihenfolge der Deklaration ausgeführt (LIFO — Last In, First Out). Das bedeutet, dass der zuletzt deklarierte defer zuerst und der erste zuletzt ausgeführt wird:
func exampleDeferOrder() {
defer { print("Erster defer") }
defer { print("Zweiter defer") }
defer { print("Dritter defer") }
print("Funktionsrumpf")
}
// Ausgabe:
// Funktionsrumpf
// Dritter defer
// Zweiter defer
// Erster defer
Die LIFO-Reihenfolge ist wichtig für die korrekte Verwaltung verschachtelter Ressourcen. Wenn Datei A zuerst geöffnet wird, dann Datei B, müssen sie in umgekehrter Reihenfolge freigegeben werden: zuerst B, dann A. Mit defer geschieht dies automatisch — deklarieren Sie defer direkt nach dem Öffnen jeder Ressource, und die Bereinigungsreihenfolge ist korrekt, unabhängig von der Anzahl der Ausstiegspunkte in der Funktion.
Laut Swift by Sundell (2024) macht diese Funktion defer ideal für verschachtelte Sperren und Transaktionen: Sperre erwerben → defer { unlock } → nächste erwerben → defer { unlock }. LIFO garantiert, dass Sperren in umgekehrter Erwerbsreihenfolge freigegeben werden, was Deadlocks verhindert.
Der Hauptanwendungsfall von defer ist die garantierte Ressourcenbereinigung. Betrachten wir die Arbeit mit dem Dateisystem. Das Öffnen einer Datei über FileHandle erfordert ein explizites Schließen — defer garantiert, dass close in jedem Szenario aufgerufen wird:
func readFile(path: String) throws -> String {
let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
defer { try? handle.close() }
let data = try handle.readToEnd()
guard let data else { throw FileError.empty() }
return String(data: data, encoding: .utf8) ?? ""
// handle.close() wird auch bei throw oder return aufgerufen
}
Ein weiteres typisches Szenario sind UI-Animationen mit einem Ladeflag. Vor dem Starten eines Ladevorgangs setzen Sie das Flag isLoading = true, und defer setzt es beim Verlassen der Funktion auf false zurück, unabhängig vom Erfolg oder Misserfolg der Anfrage. Dies verhindert, dass das Flag aufgrund eines nicht behandelten Fehlers auf true bleibt und die Oberfläche dauerhaft blockiert.
Laut dem Bitbucket Engineering Blog (2024) wird defer auch für das Profiling verwendet: Zeichnen Sie die Zeit am Anfang einer Funktion auf, und in defer — berechnen und geben Sie die Differenz aus. Dies liefert genaue Leistungsmessungen für alle Ausführungspfade, einschließlich fehlerhafter.
defer arbeitet effektiv mit throws-Funktionen zusammen. Wenn eine Funktion in jeder Phase einen Fehler werfen kann, garantiert defer die Bereinigung ohne Codeverdopplung in jedem catch-Block oder guard-frühen Ausstieg:
func processTransaction() throws {
let db = try openDatabase()
defer { closeDatabase(db) }
let user = try fetchUser(from: db)
defer { logAudit(user) }
let result = try performPayment(user)
sendNotification(result)
// closeDatabase(db) und logAudit(user) werden aufgerufen
// bei jedem throw oder return
}
Wichtig: defer wird vor der Übertragung der Kontrolle aus dem catch-Block ausgeführt, aber nach dem Auftreten des Fehlers. Wenn ein Fehler innerhalb von defer geworfen wird, erlaubt Swift die Verwendung von try nicht direkt innerhalb von defer — Sie benötigen try? oder try!. Laut Apple-Dokumentation erlaubt Swift nicht, dass ein Fehler aus defer entweicht, da dies die Garantie der Blockausführung verletzen würde.
Platzieren Sie defer unmittelbar nach der Ressourcenerfassung. Dies folgt dem Prinzip der Nähe: Der Leser sieht Erfassung und Freigabe nebeneinander, was die Codezuverlässigkeit verbessert und die Codeüberprüfung vereinfacht.
defer wird beim Verlassen des Scopes ausgeführt, in dem es deklariert ist. Wenn defer innerhalb eines do-Blocks deklariert ist, wird es beim Verlassen dieses Blocks ausgeführt, nicht der äußeren Funktion. Wenn innerhalb einer for-Schleife — bei jeder Iteration:
func scopeExample() {
print("start")
do {
defer { print("do-Block defer") }
print("inside do")
}
// "do-Block defer" wird hier ausgegeben
print("after do")
}
// Ausgabe: start, inside do, do-Block defer, after do
for i in 1...3 {
defer { print("end iteration \(i)") }
print("iteration \(i)")
}
// Ausgabe: iteration 1, end iteration 1, iteration 2, end iteration 2, ...
Von defer erfasste Variablen werden zum Zeitpunkt des Verlassens des Scopes gelesen, nicht zum Zeitpunkt der defer-Deklaration. Wenn eine Variable zwischen der defer-Deklaration und dem Ende des Scopes geändert wird, sieht defer den letzten Wert. Dies ist ein wichtiger Unterschied zu Closures, bei denen die Erfassung zum Zeitpunkt der Erstellung erfolgt. Seien Sie vorsichtig: Änderungen an einer Variable nach der Deklaration von defer beeinflussen dessen Ausführung.
Der erste Fehler — Annahme einer anderen Ausführungsreihenfolge als LIFO. Wenn die Bereinigungsreihenfolge wichtig ist und defers in der falschen Reihenfolge deklariert werden, können Ressourcen unter Verletzung von Abhängigkeiten freigegeben werden. Lösung: Deklarieren Sie defer unmittelbar nach dem Erfassen jeder Ressource. Zweite Ressource geöffnet → defer { close second } bevor die erste geschlossen wird.
Der zweite Fehler — Verwendung von defer für Logik, die nichts mit der Bereinigung zu tun hat. defer ist für garantierte Bereinigung gedacht, nicht für die Hauptflusskontrolle. Wenn Code in defer den Rückgabewert beeinflusst, ist dies fast immer ein Fehler. defer kann den Rückgabewert einer Funktion nicht ändern (im Gegensatz zu Java finally, wo return in finally das ursprüngliche return überschreibt).
Der dritte Fehler — Werfen eines Fehlers aus defer. Swift verbietet try innerhalb von defer, wenn der Fehler nach außen propagieren könnte. Verwenden Sie try? oder try! für Operationen, die einen Fehler werfen könnten, oder packen Sie sie in eine separate Funktion ohne throws. Laut O’Reilly „Swift in Depth“ (2025) ist es eine gute Praxis, Bereinigungsfunktionen non-throwing zu machen oder Fehler innerhalb von defer zu behandeln.
Häufig gestellte Fragen
defer ist eine Swift-Konstruktion, die die Ausführung eines Blocks verzögert, bis der aktuelle Scope verlassen wird. Der Block wird immer ausgeführt — bei return, throw, break oder normaler Beendigung. Es wird zur garantierten Ressourcenbereinigung verwendet: Dateien schließen, Sperren freigeben.
In umgekehrter Deklarationsreihenfolge (LIFO) — der zuletzt deklarierte defer wird zuerst ausgeführt. Dies garantiert eine korrekte Bereinigung verschachtelter Ressourcen: Wenn Ressource B nach A geöffnet wird, wird sie vor A geschlossen, was Abhängigkeiten von bereits freigegebenen Ressourcen verhindert.
Nicht direkt — Swift verhindert die Fehlerausbreitung aus defer. Verwenden Sie try? oder try! für Operationen, die einen Fehler werfen könnten. Beste Praxis ist es, Bereinigungsfunktionen non-throwing zu machen oder Fehler innerhalb von defer zu behandeln, ohne sie nach außen zu propagieren.
defer ist an einen Scope gebunden und wird bei jedem Verlassen ausgeführt, einschließlich return, throw und break. finally (in anderen Sprachen) ist an try-catch gebunden und wird nur ausgeführt, wenn try vorhanden ist. Swift hat kein finally — defer deckt dieses Szenario vollständig ab und funktioniert für jeden Scope, nicht nur für die Fehlerbehandlung.
Ja, defer liest Variablen zum Zeitpunkt des Verlassens des Scopes, nicht zum Zeitpunkt der Deklaration. Wenn eine Variable nach der Deklaration von defer geändert wird, sieht der defer-Block den letzten Wert. Dies unterscheidet sich von normalen Closures, bei denen die Erfassung zum Zeitpunkt der Erstellung fixiert wird.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch