Sobes.tech
Middle

Parlez-nous de la gestion de la mémoire dans iOS.

sobes.tech IA

Réponse de l'IA

iOS utilise le comptage automatique des références (ARC) pour gérer la mémoire. ARC libère automatiquement les objets dès qu'ils ne sont plus référencés.

Concepts principaux:

  • Référence forte: Augmente le compteur de références de l'objet. L'objet ne sera pas libéré tant qu'il existe des références fortes.
  • Référence faible: N'augmente pas le compteur de références. Utilisée pour prévenir les cycles de références fortes. Fait référence à un objet qui peut être libéré. Si l'objet est libéré, la référence faible devient nil.
  • Référence non possédée (unowned): Semblable à faible, n'augmente pas le compteur de références. Utilisée lorsque l'objet référencé a un cycle de vie égal ou plus long. La référence non possédée ne devient pas nil lors de la libération de l'objet. Tenter d'accéder à un objet libéré via une référence non possédée génère une erreur d'exécution.

Cycles de références fortes (Retain Cycles): Se produisent lorsque deux ou plusieurs objets se référencent mutuellement avec des références fortes, empêchant leur libération. Résolus en utilisant des références faibles ou non possédées.

Exemple de cycle de références fortes et sa solution:

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 cours de désinitialisation")
    }
}

class Apartment {
    let unit: String
    var tenant: Person? // Référence forte causant un cycle de rétention

    init(unit: String) {
        self.unit = unit
        print("Appartement \(unit) est en cours d'initialisation")
    }

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

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

john!.apartment = unit4A
unit4A!.tenant = john // Cycle de rétention ici

john = nil
unit4A = nil
// Les méthodes de désinitialisation ne sont pas appelées en raison du cycle de rétention

Solution avec weak:

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 cours de désinitialisation")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Utilisation de référence weak pour éviter le cycle de rétention

    init(unit: String) {
        self.unit = unit
        print("Appartement \(unit) est en cours d'initialisation")
    }

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

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

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

john = nil
unit4A = nil
// Les deux objets sont correctement désinitialisés

Non possédé (unowned) vs Faible (weak):

  • Utilise weak lorsque la référence peut devenir nil (l'objet peut être libéré en premier).
  • Utilise unowned lorsque la référence aura toujours une valeur (l'objet référencé a un cycle de vie égal ou plus long).

Closures et cycles de références:

Les closures peuvent capturer des références à des objets, créant des cycles de références fortes. Cela se résout avec une liste de capture (capture list).

Exemple:

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

    lazy var asHTML: () -> String = {
        [weak self] in // Capture weak self
        guard let self = self else {
            return ""
        }
        var html = "<\(self.name)"
        if let text = self.text {
            html += ">\(text)</\(self.name)>"
        } else {
            html += " />"
        }
        return html
    }

    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: "bonjour, monde")
print(paragraph!.asHTML())

paragraph = nil
// L'objet est désinitialisé

Débogage des problèmes de mémoire:

  • Memory Graph Debugger: Dans Xcode, permet de visualiser les connexions entre objets et de détecter les cycles de références fortes.
  • Instruments (Allocations, Leaks): Outils pour surveiller l'allocation et la libération de mémoire, et détecter les fuites.