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.