ProgrammationDéveloppeur iOS

Décrivez le mécanisme de fonctionnement de defer en Swift, avec quels cas limites et particularités il convient d'être prudent ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

defer est une construction spéciale de Swift qui permet d'exécuter un certain bloc de code juste avant de quitter la portée (scope) d'une fonction, que ce soit par un retour normal ou en cas d'erreur (throw). Defer est pratique pour libérer des ressources, annuler des modifications ou finaliser des opérations.

Particularités de fonctionnement :

  • Si une fonction a plusieurs defer, ils s'exécutent dans l'ordre inverse (structure de pile LIFO — last in, first out).
  • defer s'exécute même en cas d'erreur (throw) ou d'un retour quelconque de la fonction.
  • Dans un closure, defer est lié au corps du closure, et non à l'endroit où le closure est appelé.

Exemple :

func testDefer() { print("begin") defer { print("first defer") } defer { print("second defer") } print("end") } // Affichera : // begin // end // second defer // first defer

Cas limites :

  • defer capture les valeurs des variables au moment de la création de defer : si on utilise des variables dans le closure defer, les modifications de celles-ci seront visibles au moment de l'exécution de defer.
  • Si la fonction se termine avec fatalError, le defer ne sera pas appelé.

Question piège.

Tous les blocs defer dans la fonction seront-ils exécutés si fatalError est appelé à l'intérieur ?

Réponse : Non, si un fatalError (ou un crash incontrôlable similaire) est appelé dans la fonction, tous les blocs defer différés ne seront pas exécutés. defer ne garantit pas l'exécution du code en cas d'arrêt brutal de l'application.

Exemple :

func foo() { defer { print("Defer 1") } fatalError("Oops") defer { print("Defer 2") } } foo() // n'affichera rien, une panne se produira

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


Histoire

Dans le projet, nous avons utilisé defer pour fermer un descripteur de fichier. En cas d'erreur, nous avons utilisé fatalError au lieu d'un throw correct, ce qui entraînait une fuite de ressource ouverte, car defer ne s'exécutait pas lors d'un crash.


Histoire

Dans une fonction, il y avait plusieurs defer, dont certains dépendaient de l'état des variables locales. Le développeur s'attendait à ce qu'une variable soit égale à une valeur spécifique, mais cette valeur a changé dans le defer par un autre morceau de code, et au moment de l'exécution du defer, la valeur actuelle, et non l'ancienne, a été utilisée, entraînant un bug lors de l'annulation de la transaction avec l'ID incorrect.


Histoire

Dans un closure imbriqué, nous avons écrit un bloc defer, pensant qu'il s'exécuterait lors de la sortie de la fonction externe. En conséquence, ce defer s'est exécuté à la sortie du closure, et non de la fonction entière, et la ressource a été libérée trop tôt.