Sobes.tech
Middle

Comment prévenir la création d'un cycle de rétention (retain cycle) dans iOS?

sobes.tech IA

Réponse de l'IA

Les cycles de rétention sont évités par une utilisation correcte des références faibles (weak) ou non retenues inconditionnelles (unowned).

Références faibles (weak):

  • N'augmentent pas le compteur de références de l'objet.
  • Sont nullables (peuvent devenir nil).
  • Utilisées lorsque un objet peut exister indépendamment d'un autre (par exemple, un délégué).
class DelegateExample {
    weak var delegate: SomeDelegateType? // Référence faible au délégué
}

Références non retenues inconditionnelles (unowned):

  • N'augmentent pas le compteur de références de l'objet.
  • Garanties non nulles.
  • Utilisées lorsque un objet possède explicitement un autre, mais l'objet dépendant ne peut exister qu'avec le propriétaire (par exemple, des closures capturant self dans une classe, où la closure "appartient" explicitement à l'instance de la classe). Accéder à une référence unowned lorsque l'objet a déjà été libéré provoquera une erreur à l'exécution.
class Owner {
    var closure: (() -> Void)?

    func setupClosure() {
        closure = { [unowned self] in // Référence non retenue inconditionnelle à self
            // Utilisation de self
            print("Utilisation de self non retenu")
        }
    }
}

Utilisation dans les closures: Les closures peuvent créer des cycles de rétention s'ils capturent des instances de classes par référence forte, et cette instance de classe contient également une référence forte au closure. Pour éviter cela, on utilise des listes de capture (capture lists) au début du closure:

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = { [weak self] in // Référence faible à self
        guard let strongSelf = self else {
            return "" // Gestion du cas où self est déjà nil
        }
        if let text = strongSelf.text {
            return "<\(strongSelf.name)>\(text)</\(strongSelf.name)>"
        } else {
            return "<\(strongSelf.name)/>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) est en train d'être désinitialisé")
    }
}

Le choix entre weak et unowned dépend de la sémantique des relations entre objets:

  • weak : lorsque la référence peut devenir nil durant le cycle de vie de l'objet référencé.
  • unowned : lorsque l'objet référencé existe garantis plus longtemps ou aussi longtemps que l'objet qui le référence, et que la référence ne deviendra jamais nil.