Sobes.tech
Middle

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 weak sono 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