L'opérateur defer retarde l'exécution du code jusqu'à la sortie de l'étendue de portée actuelle, généralement d'une fonction. Il est pratique pour nettoyer des ressources, fermer des fichiers et libérer de la mémoire – ce qu'on appelle un nettoyage automatisé.
Caractéristiques :
Exemple :
func processFile() { let file = openFile() defer { closeFile(file) } // Travail avec le fichier // le fichier sera fermé même en cas de throw ou return }
Utilisation atypique : imitation partielle de constructions de type 'finally', regroupement de code pour la journalisation et le suivi des temps d'exécution.
Le defer s'exécutera-t-il si l'application se termine pendant l'exécution de la fonction ?
— Non. Le defer s'exécute uniquement lors de l'achèvement normal de l'étendue de portée (return, throw ou sortie normale). Si l'application se termine de manière inattendue (par exemple, en raison d'un signal SIGKILL ou d'une erreur fatale), les blocs defer n'auront pas le temps de s'exécuter.
Exemple :
func foo() { defer { print("Nettoyer") } fatalError("Crash !") // defer ne s'exécutera pas }
Histoire
Une fonction travaillant avec des sockets ne nettoyait pas la connexion en cas de throw. Les données restaient suspendues, les connexions n'étaient pas libérées. Après l'introduction de defer, tout fonctionnait correctement : les ressources se fermaient toujours.
Histoire
Un développeur tentait de s'appuyer sur defer pour libérer un état global. Lorsqu'un fatalError survenait, la ressource n'était pas libérée, ce qui entraînait des blocages des services et nécessitait un redémarrage.
Histoire
Dans une fonction, plusieurs defer étaient déclarés, pensant qu'ils s'exécuteraient dans l'ordre de déclaration. En conséquence, les ressources se fermaient dans le mauvais ordre (par exemple, le descripteur de fichier était fermé en premier, puis on essayait d'y accéder). Solution : se rappeler de l'ordre LIFO d'exécution des blocs defer.