Come funziona il meccanismo di gestione automatica della memoria (ARC) nel linguaggio Swift?
sobes.tech AI
Risposta dell'AI
ARC (Conteggio Automatico dei Riferimenti) monitora e gestisce automaticamente i riferimenti alle istanze delle classi. Libera la memoria occupata da un'istanza quando non ci sono più riferimenti forti ad essa. ARC funziona solo con istanze di classi, non con strutture o enumerazioni, poiché sono tipi di valore.
Tre tipi di collegamenti tra istanze:
- Strong (Forte): Un collegamento che aumenta il contatore di riferimenti dell'istanza. Per impostazione predefinita, tutti i riferimenti sono forti.
- Weak (Debole): Un collegamento che non aumenta il contatore di riferimenti. Viene usato per prevenire cicli di riferimenti forti. I riferimenti di tipo
weaksono sempre opzionali, poiché l'istanza può essere liberata in qualsiasi momento. - Unowned (Senza proprietario): Un collegamento che non aumenta il contatore di riferimenti ed è usato quando si garantisce che l'istanza collegata esisterà più a lungo di quella corrente. Non è obbligatorio, ma tentare di accedere a un'istanza liberata provocherà un errore di runtime.
I cicli di riferimenti forti si verificano quando due o più istanze di classi hanno riferimenti forti tra loro, impedendo la loro liberazione da parte di ARC. Per risolvere questi cicli, si usano riferimenti weak o unowned.
Esempio di ciclo di riferimenti forti:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // chiamato alla liberazione
print("\(name) viene deallocato")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // chiamato alla liberazione
print("Appartamento \(unit) viene deallocato")
}
}
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 // Non viene stampato nulla, perché c'è un ciclo di riferimenti forti
Risolvere il ciclo con weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) viene deallocato")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Riferimento debole a Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Appartamento \(unit) viene deallocato")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Libera Person
unit4A = nil // Libera Apartment
Risolvere con unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) viene deallocato")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Riferimento senza proprietario a Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Carta #\(number) viene deallocata")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Libera Customer e CreditCard
ARC in chiusure (closures): Le chiusure possono anche creare cicli di riferimenti forti catturando istanze di classi. Per evitarlo, si usano le liste di cattura ([weak self] o [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy per poter usare self prima dell'inizializzazione
[unowned self] in // Lista di cattura con "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) viene deallocato")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "ciao, mondo")
print(paragraph!.asHTML())
paragraph = nil // Libera HTMLElement