ProgrammationDéveloppeur iOS

Qu'est-ce que les closures escaping et non-escaping en Swift ? Comment organiser correctement le travail avec les closures, quels sont les nuances dans leur utilisation et quels risques cela peut-il entraîner lors du travail avec du code asynchrone ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Closure escaping — c'est une closure qui "s'échappe" de la portée de la fonction appelante (par exemple, elle est enregistrée pour une exécution asynchrone).

Par défaut, en Swift, les fonctions acceptent des closures non-escaping : la closure est exécutée dans le cadre de l'appel de la fonction.

Pour indiquer explicitement une escaping, on utilise le mot-clé @escaping :

func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }

Principales différences :

  • Les closures escaping peuvent être stockées et appelées plus tard.
  • Pour capturer self dans une closure escaping, il faut indiquer explicitement [weak self] ou [unowned self] pour éviter les fuites de mémoire (retain cycle).

Question piège.

Faut-il toujours écrire @escaping pour les closures passées en paramètres à une fonction appelant DispatchQueue.async ?

— Oui. Comme DispatchQueue.async conserve la closure jusqu'à son exécution, la closure devient escaping.

Exemple :

func foo(action: () -> Void) { DispatchQueue.main.async { action() // Ne compile pas : la closure doit être escaping. } } // Il faut : func bar(action: @escaping () -> Void) { ... }

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


Histoire

Le contrôleur a créé une référence forte à self à l'intérieur d'une closure escaping (par exemple, une requête réseau). Le contrôleur n'était pas libéré après avoir quitté l'écran — cycle de rétention fort. Solution : utiliser [weak self] ou [unowned self].


Histoire

La fonction a passé une closure à DispatchQueue.async, mais ne l'a pas marquée comme escaping. Le projet ne compilait pas, et les erreurs étaient difficiles à trouver à cause de la profondeur des fonctions.


Histoire

À l'intérieur de la closure, on accédait à un objet qui avait déjà été désinitialisé au moment de l'appel de la closure (utilisait [unowned self]). En conséquence — plantage à l'exécution. Solution : utiliser [weak self] et vérifier la valeur nulle.