Der defer-Operator wurde in Swift eingeführt, um Ressourcen sicher und garantiert zu bereinigen oder Code bei der Ausführung eines Blocks beim Verlassen des Sichtbereichs auszuführen, ähnlich wie finally/using/RAII in anderen Sprachen.
Mehrstufige Initialisierungen, Arbeit mit Dateien und Cross-Scenarios beim Verlassen einer Funktion erfordern eine garantierte Freigabe von Ressourcen oder das Ausführen von Logik (Datei schließen, Mutex entsperren, Objekt in den Pool zurückgeben, temporären Zustand zurücksetzen). Vor der Einführung von defer musste alles manuell in jedem Return gemacht werden.
defer garantiert die Ausführung seines Codes beim Verlassen des aktuellen Bereichs, unabhängig davon, ob der Exit normal oder durch throw geschieht. Man kann mehrere defer-Anweisungen deklarieren — sie werden in umgekehrter Reihenfolge ihrer Deklaration ausgeführt.
Beispiel zur Freigabe einer Ressource:
func processFile(path: String) throws { let file = try openFile(path) defer { file.close() } // Wird garantiert auch bei Fehlern aufgerufen // ... Arbeit mit der Datei ... }
Wesentliche Merkmale:
defer nacheinander deklarieren — sie werden in umgekehrter Reihenfolge ausgeführt,Kann defer Variablen durch Referenz erfassen und wie beeinflusst dies das Verhalten von Closures?
Ja, defer erfasst alle verwendeten Variablen zum Zeitpunkt des Aufrufs, gemäß den Regeln der Closure-Erfassung (Kopie für Werttypen, Referenz für Referenztypen). Ein Fehler wird es sein, eine veränderbare externe Wertvariable zu erfassen — sie kann sich zum Zeitpunkt der Ausführung von defer geändert haben.
Wie funktioniert geschachteltes defer innerhalb von Schleifen und Funktionen?
Wenn defer innerhalb einer Schleife deklariert ist, wird es bei jedem Abschluss einer Iteration des Bereichs ausgeführt:
for _ in 1...3 { defer { print("Ende der Iteration") } print("Innerhalb") }
Gibt aus:
Innerhalb
Ende der Iteration
Innerhalb
Ende der Iteration
Innerhalb
Ende der Iteration
Kann defer nicht ausgeführt werden?
Die Ausführung des defer-Codes ist nur garantiert, wenn der Bereich tatsächlich verlassen wird. Aber wenn der Prozess unerwartet beendet wird (fatalError, Absturz) oder exit aufgerufen wird, wird defer nicht ausgeführt. Außerdem funktioniert defer nicht auf Objektebenen — nur im Bereich von Funktionen/Blöcken.
defer für asynchrone Operationen verwenden (das letzte defer führt das Release aus, während der asynchrone Code noch nicht abgeschlossen ist),defer hintereinander deklarieren — dies verringert die Lesbarkeit und das Debugging.Im Code, nachdem die Datei geöffnet und damit gearbeitet wurde, fand der Rückgabewert über verschiedene Return/Throw-Stellen statt, wobei in einem Fall vergessen wurde, die Datei zu schließen. Infolgedessen leckte der geöffnete Dateihandle ins System.
Vorteile:
Nachteile:
Verwendung von defer für das garantierte Entsperren des Mutex, das Freigeben des Dateideskriptors und das Zurücksetzen des Fortschrittsflags:
func criticalSection() { lock() defer { unlock() } // ... Arbeit ... }
Vorteile:
Nachteile:
defer