Gestion de la mémoire en Swift : comment fonctionnent l'ARC et le ramasse-miettes

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

Pourquoi Swift n'utilise-t-il pas un ramasse-miettes traditionnel ?

\n

Si vous venez dans le monde Swift depuis Python ou Java, vous avez sûrement été surpris que ce langage n'ait pas de ramasse-miettes (Garbage Collector) classique. À la place, Apple a intégré le mécanisme Automatic Reference Counting (ARC) — le comptage automatique des références. Et ce n'est pas un simple caprice : sur les appareils mobiles, chaque cycle processeur et chaque octet de mémoire comptent. Un GC traditionnel « gèle » l'application pendant la collecte, ce qui serait catastrophique pour iOS avec son exigence de fluidité des animations (60 FPS).

\n

Dans cet article, nous allons voir comment fonctionne réellement la gestion de la mémoire en Swift, où se cachent les pièges des références cycliques, et comment écrire un code qui ne « fuit » pas.

\n\n

ARC : comment ça fonctionne sous le capot ?

\n

L'ARC n'est pas un ramasse-miettes au sens habituel. Il fonctionne à l'étape de compilation : le compilateur insère les instructions retain et release aux endroits appropriés. Mais il y a un détail : l'ARC ne sait pas gérer les références cycliques (retain cycles). Si deux objets se tiennent mutuellement par des références fortes, leur compteur de références ne tombera jamais à zéro — la mémoire fuit.

\n\n

Exemple : une classe simple et le comptage des références

\n
class Person {\n    let name: String\n    init(name: String) {\n        self.name = name\n        print("\\(name) est apparu en mémoire")\n    }\n    deinit {\n        print("\\(name) a disparu de la mémoire")\n    }\n}\n\nvar person1: Person? = Person(name: "Alice") // retain count = 1\nvar person2 = person1                        // retain count = 2\nperson1 = nil                                // retain count = 1\nperson2 = nil                                // retain count = 0 → deinit\n
\n

Dans la console, vous verrez : « Alice est apparu en mémoire » et un peu plus tard — « Alice a disparu de la mémoire ». Tout est correct.

\n\n

Références cycliques : le principal ennemi de la mémoire

\n

Imaginez : vous avez un User et un Account. L'utilisateur a un compte, et le compte connaît son propriétaire. Si les deux propriétés sont des références fortes, on obtient un cercle vicieux :

\n\n
class User {\n    let name: String\n    var account: Account?\n    init(name: String) { self.name = name }\n    deinit { print("User \\(name) supprimé") }\n}\n\nclass Account {\n    let number: String\n    var owner: User?\n    init(number: String) { self.number = number }\n    deinit { print("Account \\(number) supprimé") }\n}\n\nvar user: User? = User(name: "Boris")\nvar account: Account? = Account(number: "12345")\nuser?.account = account\naccount?.owner = user\n\nuser = nil   // User n'est pas supprimé ! account?.owner tient toujours la référence\naccount = nil // Account n'est pas supprimé ! user?.account tient la référence\n// Fuite de mémoire !\n
\n

Dans la console, aucun message deinit n'apparaîtra. Les objets restent en mémoire pour toujours. C'est un retain cycle — un problème classique de gestion de la mémoire en Swift.

\n\n

Comment le traiter ? Weak et unowned

\n

La solution est de rendre l'une des références faible (weak) ou non possédée (unowned). La différence :

\n
    \n
  • weak — toujours optionnel, devient automatiquement nil lorsque l'objet est supprimé. Sûr.
  • \n
  • unowned — non optionnel, suppose que l'objet vit plus longtemps. Si on accède à un objet supprimé — crash.
  • \n
\n\n
class Account {\n    let number: String\n    weak var owner: User?  // ← référence faible\n    init(number: String) { self.number = number }\n    deinit { print("Account \\(number) supprimé") }\n}\n// Maintenant, user = nil libérera User, et account?.owner deviendra nil\n
\n\n

Ramasse-miettes vs ARC : lequel est plus rapide ?

\n

Soyons honnêtes : l'ARC n'est pas un « ramasse-miettes » au sens classique. Un vrai GC (comme en Java, Go ou Python) fonctionne à l'exécution : il scanne périodiquement le tas, trouve les objets inaccessibles et les libère. Cela nécessite des pauses (stop-the-world).

\n

L'ARC, quant à lui, est un mécanisme stat

Blogs

Book Recommendations