Ce sunt ciclurile de retenție (retain cycles) în iOS?
sobes.tech AI
Răspuns de la AI
Ciclurile de retenție apar atunci când două sau mai multe obiecte au referințe puternice între ele, creând un "cerc închis". Ca urmare, contorul de referințe al fiecărui obiect nu ajunge niciodată la zero și nu poate fi eliberat de ARC (Numărătoarea Automată a Referințelor). Acest lucru duce la scurgeri de memorie, deoarece obiectele rămân în memorie chiar și după ce nu mai sunt utilizate.
Exemplu:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) este inițializat")
}
deinit {
print("\(name) este deinitializat")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartament \(unit) este inițializat")
}
deinit {
print("Apartament \(unit) este deinitializat")
}
}
În acest exemplu, dacă Person are o referință puternică către Apartment, și Apartment are o referință puternică către Person, aceste obiecte nu vor fi eliminate atunci când se încearcă eliberarea lor, deoarece contoarele de referințe nu ating zero.
Cum se rezolvă problema ciclurilor de retenție:
Folosiți referințe weak sau unowned.
weak: Se folosește atunci când obiectul asociat poate deveninilpe parcursul ciclului de viață al altui obiect. Referința devinenilautomat când obiectul la care face referire este eliberat. Este opțională.unowned: Se folosește atunci când se știe că obiectul asociat are aceeași sau o durată de viață mai lungă decât obiectul care îl deține. Nu poate finil.
Exemplu corectat folosind weak:
Să schimbăm referința tenant în clasa Apartment la una weak:
class Apartment {
let unit: String
weak var tenant: Person? // folosim weak
init(unit: String) {
self.unit = unit
print("Apartament \(unit) este inițializat")
}
deinit {
print("Apartament \(unit) este deinitializat")
}
}
Acum, când obiectul Person este eliberat, referința tenant în Apartment devine automat nil, rupând ciclul de retenție și permițând și Apartment să fie eliberat.
Tabel comparativ weak și unowned:
| Caracteristică | weak |
unowned |
|---|---|---|
| Tip de referință | Opțional | Non-opțional |
| Poate fi nil? | Da, când obiectul este eliberat | Nu |
| Când să folosiți | Când obiectul poate deveni nil |
Când știți că obiectul nu va fi nil |
| Performanță | Puțin mai lent din cauza verificării nil |
Puțin mai rapid |
De asemenea, ciclurile de retenție pot apărea și în closures.
Când o closure capturează proprietăți ale instanței sau self, poate crea o referință puternică către obiect. Dacă acest obiect are, de asemenea, o referință puternică către closure (de exemplu, printr-o proprietate de tip closure sau delegat), se produce un ciclu.
Cum să rezolvați ciclurile de retenție în closures:
Folosiți lista de captură (capture list).
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // listă de captură cu weak self
guard let self = self else { return } // verificare nil pentru weak self
self.value += 1
print("Valoarea este acum \(self.value)")
}
}
}
În acest exemplu, [weak self] creează o referință slabă la self în interiorul closure-ului, prevenind un ciclu de retenție.