De operator defer stelt de uitvoering van code uit tot het verlaten van de huidige scope, meestal uit een functie. Het is handig voor het opruimen van bronnen, het sluiten van bestanden en het vrijgeven van geheugen – de zogenaamde geautomatiseerde cleanup.
Kenmerken:
Voorbeeld:
func processFile() { let file = openFile() defer { closeFile(file) } // Werken met het bestand // file wordt gesloten zelfs in het geval van throw of return }
Ongebruikelijke toepassing: gedeeltelijke simulatie van constructies zoals 'finally', groeperen van code voor logging en het bijhouden van verwerkingstijden.
Wordt defer uitgevoerd als de applicatie tijdens het uitvoeren van de functie stopt?
— Nee. Defer wordt alleen uitgevoerd bij normale beëindiging van de scope (return, throw of normale exit). Als de applicatie onverwacht stopt (bijvoorbeeld door een SIGKILL-signaal of fatalError), hebben defer-blokken geen kans om uitgevoerd te worden.
Voorbeeld:
func foo() { defer { print("Opruimen") } fatalError("Crash!") // defer wordt niet uitgevoerd }
Verhaal
Een functie die met sockets werkte, maakte de verbinding niet vrij in het geval van throw. Gegevens bleven hangen, verbindingen werden niet vrijgegeven. Na het invoegen van defer werkte alles correct: bronnen werden altijd gesloten.
Verhaal
Een ontwikkelaar probeerde te vertrouwen op defer voor het vrijgeven van globale staat. Bij het optreden van fatalError werd de bron niet vrijgegeven, wat leidde tot blokkeringen van services en de noodzaak tot herstarten.
Verhaal
In de functie werden meerdere defer gedeclareerd, in de veronderstelling dat ze in volgorde van verklaring zouden worden uitgevoerd. Als gevolg hiervan werden de bronnen in de verkeerde volgorde gesloten (bijvoorbeeld eerst werd de bestandsdescriptor gesloten, en daarna werd geprobeerd deze aan te roepen). Oplossing: onthoud de LIFO-volgorde van uitvoering van defer-blokken.