Sobes.tech
Middle

Comment fonctionne le mécanisme de gestion automatique de la mémoire (ARC) dans le langage Swift ?

sobes.tech IA

Réponse de l'IA

ARC (Comptage automatique des références) suit et gère automatiquement les références aux instances de classes. Il libère la mémoire occupée par une instance lorsqu'il n'y a plus de références fortes vers elle. ARC ne fonctionne qu'avec des instances de classes, pas avec des structures ou des énumérations, car ce sont des types de valeur.

Trois types de liens entre instances :

  • Strong (Fort) : Un lien qui augmente le compteur de références de l'instance. Par défaut, toutes les références sont fortes.
  • Weak (Faible) : Un lien qui n'augmente pas le compteur de références. Utilisé pour éviter les cycles de références fortes. Les références de type weak sont toujours optionnelles, car l'instance peut être libérée à tout moment.
  • Unowned (Sans propriétaire) : Un lien qui n'augmente pas le compteur de références et est utilisé lorsque l'on garantit que l'instance liée existera plus longtemps que l'actuelle. Ce n'est pas obligatoire, mais tenter d'accéder à une instance libérée provoquera une erreur d'exécution.

Les cycles de références fortes se produisent lorsque deux ou plusieurs instances de classes ont des références fortes entre elles, empêchant leur libération par ARC. Pour résoudre ces cycles, on utilise des références weak ou unowned.

Exemple de cycle de références fortes :

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
    }

    deinit { // appelé lors de la libération
        print("\(name) est en cours de désinitialisation")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
    }

    deinit { // appelé lors de la libération
        print("Appartement \(unit) est en cours de désinitialisation")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil
unit4A = nil // Rien n'est imprimé, car il y a une boucle de références fortes

Résolution de la boucle avec weak :

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\(name) est en cours de désinitialisation")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Référence faible à Person

    init(unit: String) {
        self.unit = unit
    }

    deinit {
        print("Appartement \(unit) est en cours de désinitialisation")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // Libère Person
unit4A = nil // Libère Apartment

Résolution avec unowned :

class Customer {
    let name: String
    var card: CreditCard?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\(name) est en cours de désinitialisation")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Référence sans propriétaire à Customer

    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }

    deinit {
        print("Carte #\(number) est en cours de désinitialisation")
    }
}

var john: Customer?

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

john = nil // Libère Customer et CreditCard

ARC dans les closures : Les closures peuvent également créer des cycles de références fortes en capturant des instances de classes. Pour éviter cela, on utilise des listes de capture ([weak self] ou [unowned self]).

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

    lazy var asHTML: () -> String = { // lazy pour pouvoir utiliser self avant l'initialisation
        [unowned self] in // Liste de capture avec "unowned self"
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

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

    deinit {
        print("\(name) est en cours de désinitialisation")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil // Libère HTMLElement