Sobes.tech
Middle

Parlaci della gestione della memoria in iOS.

sobes.tech AI

Risposta dell'AI

iOS utilizza il conteggio automatico delle referenze (ARC) per gestire la memoria. ARC libera automaticamente gli oggetti non più referenziati.

Concetti principali:

  • Riferimento forte: Aumenta il contatore di riferimenti dell'oggetto. L'oggetto non sarà liberato finché ci sono riferimenti forti.
  • Riferimento debole: Non aumenta il contatore di riferimenti. Usato per prevenire cicli di riferimenti forti. Si riferisce a un oggetto che può essere liberato. Se l'oggetto viene liberato, il riferimento debole diventa nil.
  • Riferimento non posseduto (unowned): Simile a debole, non aumenta il contatore di riferimenti. Usato quando l'oggetto referenziato ha un ciclo di vita uguale o più lungo. Il riferimento non posseduto non diventa nil quando l'oggetto viene liberato. Tentare di accedere a un oggetto rilasciato tramite un riferimento non posseduto genera un errore di runtime.

Cicli di riferimenti forti (Retain Cycles): Si verificano quando due o più oggetti si riferiscono reciprocamente con riferimenti forti, impedendo che vengano liberati. Si risolvono usando riferimenti deboli o non posseduti.

Esempio di ciclo di riferimenti forti e sua soluzione:

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

    init(name: String) {
        self.name = name
        print("\(name) viene inizializzato")
    }

    deinit {
        print("\(name) viene de-inizializzato")
    }
}

class Apartment {
    let unit: String
    var tenant: Person? // Riferimento forte che causa ciclo di ritenzione

    init(unit: String) {
        self.unit = unit
        print("Appartamento \(unit) viene inizializzato")
    }

    deinit {
        print("Appartamento \(unit) viene de-inizializzato")
    }
}

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

john!.apartment = unit4A
unit4A!.tenant = john // Qui si verifica un ciclo

john = nil
unit4A = nil
// I metodi di de-inizializzazione non vengono chiamati a causa del ciclo

Soluzione usando weak:

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

    init(name: String) {
        self.name = name
        print("\(name) viene inizializzato")
    }

    deinit {
        print("\(name) viene de-inizializzato")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Usa riferimento weak per prevenire ciclo

    init(unit: String) {
        self.unit = unit
        print("Appartamento \(unit) viene inizializzato")
    }

    deinit {
        print("Appartamento \(unit) viene de-inizializzato")
    }
}

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

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

john = nil
unit4A = nil
// Entrambi gli oggetti vengono de-inizializzati correttamente

Non posseduto (unowned) vs Debole (weak):

  • Usa weak quando la referenza può diventare nil (l'oggetto può essere rilasciato per primo).
  • Usa unowned quando la referenza avrà sempre un valore (l'oggetto referenziato ha un ciclo di vita uguale o più lungo).

Chiusure (Closures) e cicli di riferimenti:

Le chiusure possono catturare riferimenti agli oggetti, creando cicli di riferimenti forti. Questo si risolve con una lista di cattura (capture list).

Esempio:

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

    lazy var asHTML: () -> String = {
        [weak self] in // Cattura debole di 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) viene de-inizializzato")
    }
}

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

paragraph = nil
// Oggetto de-inizializzato

Debug di problemi di memoria:

  • Memory Graph Debugger: In Xcode, permette di visualizzare le connessioni tra gli oggetti e di identificare cicli di riferimenti forti.
  • Instruments (Allocations, Leaks): Strumenti per monitorare l'allocazione e il rilascio della memoria, e per individuare perdite.