Sobes.tech
Middle

Come prevenire la creazione di un ciclo di ritenzione (retain cycle) in iOS?

sobes.tech AI

Risposta dell'AI

I cicli di ritenzione vengono evitati mediante l'uso corretto di riferimenti deboli (weak) o non trattenuti incondizionatamente (unowned).

Riferimenti deboli (weak):

  • Non aumentano il contatore di riferimenti dell'oggetto.
  • Sono nullable (possono diventare nil).
  • Sono usate quando un oggetto può esistere indipendentemente da un altro (ad esempio, un delegato).
class DelegateExample {
    weak var delegate: SomeDelegateType? // Riferimento debole al delegato
}

Riferimenti non trattenuti incondizionatamente (unowned):

  • Non aumentano il contatore di riferimenti dell'oggetto.
  • Sono garantiti non nulli.
  • Sono usati quando un oggetto possiede esplicitamente un altro, ma l'oggetto dipendente può esistere solo insieme al proprietario (ad esempio, chiusure che catturano self in una classe, dove la chiusura "appartiene" esplicitamente all'istanza della classe). Accedere a una referenza unowned quando l'oggetto è già stato deallocato causerà un errore in fase di esecuzione.
class Owner {
    var closure: (() -> Void)?

    func setupClosure() {
        closure = { [unowned self] in // Riferimento non trattenuto incondizionatamente a self
            // Uso di self
            print("Utilizzo di self non trattenuto")
        }
    }
}

Utilizzo nei closure: I closure possono creare cicli di ritenzione se catturano istanze di classi con riferimenti forti, e questa istanza di classe contiene anche un riferimento forte al closure. Per evitarlo, si usano le liste di cattura (capture lists) all'inizio del closure:

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

    lazy var asHTML: () -> String = { [weak self] in // Riferimento debole a self
        guard let strongSelf = self else {
            return "" // Gestione del caso in cui self è già nil
        }
        if let text = strongSelf.text {
            return "<\(strongSelf.name)>\(text)</\(strongSelf.name)>"
        } else {
            return "<\(strongSelf.name)/>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) viene deallocato")
    }
}

La scelta tra weak e unowned dipende dalla semantica delle relazioni tra gli oggetti:

  • weak: quando la referenza può diventare nil durante il ciclo di vita dell'oggetto referenziato.
  • unowned: quando l'oggetto referenziato esiste garantito più a lungo o uguale a quello che lo riferisce, e la referenza non diventerà mai nil.