Qu'est-ce que les cycles de rétention (retain cycles) dans iOS?
sobes.tech IA
Réponse de l'IA
Les cycles de rétention se produisent lorsque deux objets ou plus ont des références fortes l'un à l'autre, créant ainsi un "cercle fermé". En conséquence, le compteur de références de chaque objet n'atteint jamais zéro, et il ne peut pas être libéré par ARC (Automatic Reference Counting). Cela entraîne des fuites de mémoire, car les objets restent en mémoire même après avoir cessé d'être utilisés.
Exemple :
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) est en cours d'initialisation")
}
deinit {
print("\(name) est en train d'être désinitialisé")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Appartement \(unit) est en cours d'initialisation")
}
deinit {
print("Appartement \(unit) est en train d'être désinitialisé")
}
}
Dans cet exemple, si Person a une référence forte à Apartment, et Apartment a une référence forte à Person, lors de la tentative de libération de ces objets, ils ne seront pas supprimés, car leurs compteurs de références ne atteignent pas zéro.
Comment résoudre le problème des cycles de rétention :
Utilisez des références faibles (weak) ou sans propriétaire (unowned).
weak: Utilisé lorsque l'objet lié peut devenirnilpendant le cycle de vie d'un autre objet. La référence devient automatiquementnillorsque l'objet référencé est libéré. C'est une option.unowned: Utilisé lorsque l'objet lié a la même ou une durée de vie plus longue que l'objet qui le référence. Ne peut pas êtrenil.
Exemple corrigé avec weak :
Changeons la référence tenant dans la classe Apartment en faible :
class Apartment {
let unit: String
weak var tenant: Person? // Utilisation de weak
init(unit: String) {
self.unit = unit
print("Appartement \(unit) est en cours d'initialisation")
}
deinit {
print("Appartement \(unit) est en train d'être désinitialisé")
}
}
Maintenant, lorsque l'objet Person est libéré, la référence tenant dans Apartment devient automatiquement nil, rompant ainsi le cycle de rétention et permettant à Apartment d'être également libéré.
Tableau comparatif de weak et unowned :
| Caractéristique | weak |
unowned |
|---|---|---|
| Type de référence | Optionnel | Non optionnel |
| Peut être nil ? | Oui, lors de la libération de l'objet | Non |
| Quand utiliser | Lorsque l'objet peut être nil |
Lorsque l'objet ne peut pas être nil |
| Performance | Légèrement plus lent en raison de la vérification de nil |
Légèrement plus rapide |
De plus, les cycles de rétention peuvent également se produire dans les closures.
Lorsqu'une closure capture des propriétés d'une instance ou self, elle peut créer une référence forte à l'objet. Si cet objet a également une référence forte à la closure (par exemple, via une propriété de closure ou un délégué), un cycle de rétention se produit.
Comment résoudre les cycles de rétention dans les closures :
Utilisez la liste de capture (capture list).
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // Liste de capture avec weak self
guard let self = self else { return } // Vérification de nil pour weak self
self.value += 1
print("La valeur est maintenant \(self.value)")
}
}
}
Dans cet exemple, [weak self] crée une référence faible à self dans la closure, évitant ainsi un cycle de rétention.