Sobes.tech
Middle

Este întotdeauna necesar să folosiți weak sau unowned în lista de captură?

sobes.tech AI

Răspuns de la AI

Nu, nu întotdeauna.

Folosim weak sau unowned în Capture List pentru a preveni ciclurile de retenție.

Ciclul de retenție apare atunci când două sau mai multe obiecte se referă puternic unul pe celălalt, împiedicând eliberarea memoriei.

Este necesar să folosiți weak sau unowned atunci când o închidere face referire puternică la self (sau altă variabilă de instanță a clasei/actorului), și la rândul său self (sau alt obiect) are o referință puternică la această închidere.

Exemple de situații în care este necesar:

  • Operațiuni asincrone (solicitări de rețea, procesare de date)
  • Pattern-ul Observer
  • Blocuri de animație
  • Cozi de dispatch

Dacă închiderea nu capturează self (sau altă variabilă de clasă/actor, care are o referință puternică la închidere), sau capturează tipuri structurale/enumerări, utilizarea weak sau unowned nu este necesară.

Diferența dintre weak și unowned:

Tip Descriere Când să folosiți
weak Referință slabă (opțională). Poate deveni nil. Când ciclul de viață al închiderii este mai scurt sau egal cu cel al obiectului capturat, și obiectul poate deveni nil înainte de finalizarea închiderii.
unowned Referință neprincipală (neopțională). Nu poate deveni nil. Când ciclul de viață al închiderii este mai scurt sau egal cu cel al obiectului capturat, și garantat că obiectul va exista până la finalizarea închiderii. Dacă obiectul este eliberat înainte de executarea închiderii, va avea loc o eroare (referință unowned către o instanță dezalocată).

Exemplu de utilizare weak în Capture List:

class MyClass {
    var name: String = "Test"

    func performAsyncOperation() {
        // Închidere capturează self.
        // MyClass poate fi eliberat înainte de finalizarea operației asincrone.
        DispatchQueue.global().async { [weak self] in
            guard let self = self else {
                // self a fost eliberat, ieșim
                return
            }
            print("Operație finalizată pentru \(self.name)")
        }
    }

    deinit {
        print("MyClass dezalocat")
    }
}

Exemplu de utilizare unowned în Capture List:

class ViewController {
    var dismissButton: UIButton!

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

        // Închidere capturează self.
        // Presupunem că `self` (ViewController) trăiește mai mult sau egal cu acțiunea butonului.
        // În acest caz, target-action nu este o închidere, dar ilustrează conceptul de unowned în cazul ciclului de retenție puternic.
        // Dacă ar fi fost o închidere, de exemplu, în handler-ul UIControl.Event,
        // și ViewController există garantat în timpul apăsării butonului:
        // dismissButton.addAction(UIAction { [unowned self] _ in
        //     self.dismiss(animated: true, completion: nil)
        // }, for: .touchUpInside)
    }

    @objc func dismiss() {
        // Închidere de dismiss view controller
    }
}

Dacă închiderea nu creează un ciclu de retenție, utilizarea weak sau unowned nu este obligatorie și poate chiar duce la verificări inutile (în cazul weak) sau la potențiale erori (în cazul unowned).

De exemplu, o închidere care nu capturează self sau alt obiect, care are o referință puternică către închidere:

func fetchData(completion: @escaping (Data) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
        guard let data = data else { return }
        // Aici nu există capturare de self sau alte obiecte care pot cauza cicluri de retenție.
        completion(data)
    }.resume()
}

În acest caz, [weak self] sau [unowned self] în Capture List nu sunt necesare.