Mi azok a megtartási ciklusok (retain cycles) az iOS-ben?
sobes.tech MI
Válasz az MI-től
Visszatartási ciklusok akkor keletkeznek, amikor két vagy több objektum erős hivatkozásokkal rendelkezik egymásra, így egy "zárt kör" alakul ki. Ennek eredményeként minden objektum hivatkozásszámlálója soha nem ér el nullát, és nem szabadulhat fel az ARC (Automatikus Hivatkozás Számlálás) által. Ez memória szivárgáshoz vezet, mivel az objektumok akkor is a memóriában maradnak, ha már nem használják őket.
Példa:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) inicializálódik")
}
deinit {
print("\(name) felszabadul")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) inicializálódik")
}
deinit {
print("Apartment \(unit) felszabadul")
}
}
Ebben a példában, ha a Person erős hivatkozással rendelkezik az Apartment-re, és az Apartment erős hivatkozással rendelkezik a Person-re, akkor az objektumok felszabadításakor nem kerülnek törlésre, mivel hivatkozásszámlálóik nem érnek el nullát.
Hogyan lehet megoldani a visszatartási ciklusokat:
Használjon gyenge (weak) vagy szehetetlen (unowned) hivatkozásokat.
weak: Akkor használjuk, amikor az összekapcsolt objektumnillehet a másik objektum életciklusán belül. A hivatkozás automatikusannillesz, amikor az objektum, amelyre hivatkozik, felszabadul. Ez opcionális.unowned: Akkor használjuk, amikor tudjuk, hogy az összekapcsolt objektumnak ugyanakkor vagy hosszabb életciklusa van, mint a hivatkozó objektumnak. Nem lehetnil.
Javított példa weak használatával:
Az Apartment osztályban a tenant hivatkozást gyenge hivatkozásra változtatjuk:
class Apartment {
let unit: String
weak var tenant: Person? // weak használata
init(unit: String) {
self.unit = unit
print("Apartment \(unit) inicializálódik")
}
deinit {
print("Apartment \(unit) felszabadul")
}
}
Most, amikor a Person objektum felszabadul, a tenant hivatkozás az Apartment-ben automatikusan nil lesz, megszakítva a visszatartási ciklust, és lehetővé téve az Apartment felszabadulását is.
A weak és unowned összehasonlítása:
| Jellemző | weak |
unowned |
|---|---|---|
| Hivatkozás típusa | Opciókénti | Nem opcionális |
nil lehet? |
Igen, amikor az objektum felszabadul | Nem |
| Mikor használjuk? | Ha az objektum nil lehet |
Ha az objektum nem lehet nil |
| Teljesítmény | Kicsit lassabb a nil ellenőrzése miatt |
Gyorsabb |
Ezenkívül, a visszatartási ciklusok előfordulhatnak záradékokban is.
Amikor egy záradék rögzíti az instance tulajdonságait vagy a self-t, akkor erős hivatkozást hozhat létre az objektumra. Ha ez az objektum is erős hivatkozással rendelkezik a záradékra (pl. tulajdonság-záradék vagy delegált révén), ciklus alakul ki.
Hogyan lehet megoldani a visszatartási ciklusokat záradékokban:
Használjon fogási listát (capture list).
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // gyenge fogási lista
guard let self = self else { return } // `nil` ellenőrzése a gyenge `self`-re
self.value += 1
print("Az érték most \(self.value)")
}
}
}
Ebben a példában, a [weak self] gyenge hivatkozást hoz létre a self-re a záradékban, elkerülve a ciklus kialakulását.