Sobes.tech
Middle

È sempre necessario usare weak o unowned nella lista di cattura?

sobes.tech AI

Risposta dell'AI

No, non sempre.

Utilizziamo weak o unowned nella Capture List per prevenire cicli di ritenzione.

Un ciclo di ritenzione si verifica quando due o più oggetti si riferiscono fortemente l'uno all'altro, impedendo che la memoria venga liberata.

Si dovrebbe usare weak o unowned quando una closure fa riferimento forte a self (o ad un'altra variabile di istanza di classe/attore), e a sua volta self (o un altro oggetto) ha una referenza forte a questa closure.

Esempi di situazioni in cui ciò è necessario:

  • Operazioni asincrone (richieste di rete, elaborazione dati)
  • Pattern Observer
  • Blocchi di animazione
  • Code di dispatch

Se la closure non cattura self (o un'altra variabile di classe/attore che ha una referenza forte alla closure), o cattura tipi strutturali/enumerazioni, l'uso di weak o unowned non è necessario.

Differenze tra weak e unowned:

Tipo Descrizione Quando usare
weak Riferimento debole (opzionale). Può diventare nil. Quando il ciclo di vita della closure è più breve o uguale a quello dell'oggetto catturato, e l'oggetto può diventare nil prima di completare la closure.
unowned Riferimento non opzionale. Non può diventare nil. Quando il ciclo di vita della closure è più breve o uguale a quello dell'oggetto catturato, e si garantisce che l'oggetto esisterà fino al termine della closure. Se l'oggetto viene deallocato prima dell'esecuzione della closure, si verifica un crash (riferimento unowned a un'istanza deallocata).

Esempio di utilizzo di weak nella Capture List:

class MyClass {
    var name: String = "Test"

    func performAsyncOperation() {
        // La closure cattura `self`.
        // MyClass può essere deallocato prima che l'operazione asincrona finisca.
        DispatchQueue.global().async { [weak self] in
            guard let self = self else {
                // `self` è stato deallocato, uscire
                return
            }
            print("Operazione completata per \(self.name)")
        }
    }

    deinit {
        print("MyClass deallocato")
    }
}

Esempio di utilizzo di unowned nella Capture List:

class ViewController {
    var dismissButton: UIButton!

    func setupButton() {
        dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)

        // La closure cattura `self`.
        // Supponiamo che `self` (ViewController) viva più a lungo o uguale all'azione del pulsante.
        // In questo caso, il target-azione non è una closure, ma illustra il concetto di `unowned` in cicli di riferimento forti.
        // Se fosse *una* closure, ad esempio, nell'handler di UIControl.Event,
        // e ViewController esiste durante il clic:
        // dismissButton.addAction(UIAction { [unowned self] _ in
        //     self.dismiss(animated: true, completion: nil)
        // }, for: .touchUpInside)
    }

    @objc func dismiss() {
        // Chiudere il view controller
    }
}

Se la closure non crea un ciclo di ritenzione, l'uso di weak o unowned non è necessario e potrebbe anche portare a controlli non necessari (weak) o potenziali crash (unowned).

Ad esempio, una closure che non cattura self o un altro oggetto, che ha una referenza forte che la closure possiede:

func fetchData(completion: @escaping (Data) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
        guard let data = data else { return }
        // Qui non c'è cattura di `self` o altri oggetti che possano causare un ciclo di ritenzione.
        completion(data)
    }.resume()
}

In questo caso, [weak self] o [unowned self] nella Capture List non sono necessari.