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
weaksont 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