[unowned self] en Swift : quand est-il nécessaire et quand est-il dangereux ?

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Le dilemme éternel : mettre [unowned self] ou pas ?

\n\n

Si vous avez déjà programmé en Swift et rencontré les closures, vous vous êtes sûrement demandé : « Dois-je écrire [unowned self] dans chaque closure où j’utilise self ? ».

\n\n

C’est l’une des questions les plus populaires sur Stack Overflow (des dizaines de milliers de vues), et pour cause. En 2025, avec l’arrivée de nouvelles fonctionnalités Swift (comme un analyseur de cycle de vie amélioré et des vérifications plus strictes du compilateur), la réponse est devenue encore plus nuancée.

\n\n

Dans cet article, nous ne donnons pas simplement une réponse sèche « oui » ou « non ». Nous analysons comment fonctionne réellement la gestion de la mémoire en Swift, quand [unowned self] est une bouée de sauvetage et quand c’est une bombe à retardement, et quelles sont les meilleures pratiques à utiliser dans les projets modernes.

\n\n

C’est parti ! 🚀

\n\n

Bref aperçu de l’ARC et des cycles de rétention (pour les débutants)

\n\n

Avant de parler de [unowned self], rappelons comment fonctionne l’Automatic Reference Counting (ARC).

\n\n

L’ARC est un mécanisme de comptage de références. Chaque objet en Swift (instance d’une classe) possède un compteur de références. Lorsque vous créez une nouvelle référence vers un objet (par exemple, en l’affectant à une propriété ou en le mettant dans un tableau), le compteur augmente. Lorsque la référence disparaît, il diminue. Lorsque le compteur atteint zéro, l’objet est détruit.

\n\n

Le problème survient lorsque deux objets se référencent mutuellement (référence cyclique). Imaginez :

\n\n
class Parent {\n    var child: Child?\n}\n\nclass Child {\n    var parent: Parent? // Référence forte vers Parent\n}
\n\n

Si vous créez une paire Parent et Child et définissez les deux références, ils se retiendront mutuellement pour toujours. L’ARC ne mettra jamais leurs compteurs à zéro. C’est ce qu’on appelle un cycle de rétention (retain cycle).

\n\n

Les closures sont aussi des types par référence. Lorsqu’une closure capture self (par exemple, pour appeler une méthode), elle stocke une référence forte vers l’objet. Si l’objet, à son tour, stocke une référence forte vers la closure (par exemple, comme propriété), le même cycle se produit.

\n\n

Weak vs Unowned : quelle est la différence ?

\n\n

Pour briser le cycle, nous utilisons weak ou unowned.

\n\n

weak self

\n
    \n
  • Toujours optionnel (self?).
  • \n
  • La référence n’augmente pas le compteur ARC.
  • \n
  • Lorsque l’objet est détruit, la référence weak devient automatiquement nil.
  • \n
  • Sûr — vous vérifiez toujours guard let self = self else { return }.
  • \n
\n\n

unowned self

\n
    \n
  • Non optionnel (self sans point d’interrogation).
  • \n
  • La référence n’augmente pas le compteur ARC.
  • \n
  • Lorsque l’objet est détruit, la référence unowned ne devient pas nil. Elle devient une référence pendante (dangling pointer).
  • \n
  • L’accès à une telle référence après la destruction de l’objet provoque un crash (EXC_BAD_ACCESS).
  • \n
\n\n

Point clé : unowned est une promesse faite au compilateur : « Je garantis que l’objet sera vivant tant que cette closure s’exécute ». Si vous vous trompez, l’application plante.

\n\n

Quand utiliser [unowned self] (et quand ne pas le faire)

\n\n

Analysons des scénarios réels de la pratique du développement iOS en 2025.

\n\n

Cas 1 : [unowned self] est OK (et même préférable)

\n\n

Utilisez [unowned self] lorsque vous êtes absolument certain que self sera vivant au moment de l’exécution de la closure. Un exemple typique est une closure qui s’exécute de manière synchrone à l’intérieur d’une méthode de l’objet, et où l’objet est garanti de ne pas être détruit avant la fin de la closure.

\n\n
class DataFetcher {\n    var data: Data?\n    \n    func fetchData() {\n        // Cette closure s’exécute de manière synchrone à l’intérieur de la méthode\n        let processCl                

Blogs

Book Recommendations