ProgrammationDéveloppeur iOS

Expliquez le fonctionnement des closures en Swift : différence par rapport aux fonctions, caractéristiques de la capture des variables, problèmes potentiels en cas d'utilisation incorrecte.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Les closures en Swift sont des blocs de code autonomes qui peuvent capturer et conserver des références à des variables et des constantes du contexte environnant. Elles permettent d'organiser des callbacks, de gérer des traitements asynchrones et de stocker l'exécution de code en tant que valeurs de variables.

Différences par rapport aux fonctions

  • Les closures peuvent capturer des variables de l'extérieur de leur portée.
  • Elles ont une syntaxe plus concise.
  • Elles peuvent être passées comme valeurs.

Exemple de closure :

var counter = 0 let incrementer: () -> Void = { counter += 1 } incrementer() print(counter) // 1

Caractéristiques de la capture

  • Par défaut, les variables sont capturées strong (par référence forte).
  • Il est possible de définir explicitement les règles de capture via la capture list ([weak self], [unowned self]).

Problèmes

  • Le principal problème est les cycles de rétention possibles lors de la capture de self à l'intérieur de la closure.
  • Dans le code multithread, il est possible de capturer sans le vouloir des valeurs obsolètes de variables.

Question piégée

Quelle est la différence entre la capture forte et l'utilisation de [weak self] ou [unowned self] dans la capture list d'une closure ? Quelles en sont les conséquences ?

Réponse : Si vous ne spécifiez pas [weak self] ou [unowned self], la closure capturera par défaut self par référence forte, ce qui conduira à un cycle de rétention si la closure et self se référencent mutuellement. [weak self] capture self par référence faible (optional), [unowned self] par référence non contrôlée (non-optional). Un choix incorrect ou l'absence de capture list entraîne des fuites de mémoire ou des plantages.

class Test { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doWork() } } func doWork() { ... } }

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet


Histoire

Un programmeur n'a pas utilisé de capture list dans une closure à l'intérieur d'un UIViewController, qui lançait le chargement asynchrone de données. En conséquence, le contrôleur et sa vue n'étaient pas libérés, ce qui a entraîné une fuite de mémoire après la fermeture (dismiss). L'analyse de l'infrastructure a révélé des centaines de "contrôleurs suspendus" en mémoire.


Histoire

La closure a capturé self avec [unowned self], mais le cycle de vie de self s'est terminé avant celui de la closure. Cela a provoqué un crash lors de l'accès à un objet déjà libéré — l'application plantait lors de l'interaction avec self à l'intérieur de la closure.


Histoire

Dans le projet, à l'intérieur de closures imbriquées, deux variables ont été capturées : une par référence forte, l'autre par référence faible. Il s'est avéré que la référence faible était nulled trop tôt, et les données nécessaires au fonctionnement de la closure ont été perdues — le bug ne s'est manifesté que lors d'un stress-test en charge dans un environnement multithread.