Čo sú to cykly udržiavania (retain cycles) v iOS?
sobes.tech AI
Odpoveď od AI
Cyklusy zadržania vznikajú, keď dva alebo viac objektov majú silné odkazy na seba navzájom, čím vytvárajú "uzavretý kruh". V dôsledku toho počet odkazov na každý objekt nikdy nedosiahne nulu a nemôže byť uvoľnený ARC (Automatic Reference Counting). To vedie k únikom pamäte, pretože objekty zostávajú v pamäti aj po ukončení používania.
Príklad:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) sa inicializuje")
}
deinit {
print("\(name) sa uvoľňuje")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) sa inicializuje")
}
deinit {
print("Apartment \(unit) sa uvoľňuje")
}
}
V tomto príklade, ak má Person silnú referenciu na Apartment a Apartment má silnú referenciu na Person, pri pokuse o uvoľnenie týchto objektov nebudú odstránené, pretože ich počítadlá odkazov nebudú dosahovať nulu.
Ako vyriešiť problém cyklov uchovávania:
Použite slabé (weak) alebo nepovinné (unowned) odkazy.
weak: Používa sa, keď je možné, že súvisiaci objekt sa stanenilpočas životného cyklu iného objektu. Odkaz sa automaticky stanenil, keď sa objekt, na ktorý odkazuje, uvoľní. Je voliteľný.unowned: Používa sa, keď je známe, že súvisiaci objekt má rovnaký alebo dlhší životný cyklus ako objekt, ktorý na neho odkazuje. Nemôže byťnil.
Opravený príklad s použitím weak:
Zmeníme odkaz tenant v triede Apartment na slabý:
class Apartment {
let unit: String
weak var tenant: Person? // používame weak
init(unit: String) {
self.unit = unit
print("Apartment \(unit) sa inicializuje")
}
deinit {
print("Apartment \(unit) sa uvoľňuje")
}
}
Teraz, keď sa Person objekt uvoľní, odkaz tenant v Apartment sa automaticky stane nil, čím sa preruší cyklus uchovávania a umožní aj uvoľnenie Apartment.
Porovnanie weak a unowned:
| Charakteristika | weak |
unowned |
|---|---|---|
| Typ odkazu | Voliteľný | Nevoliteľný |
Môže byť nil? |
Áno, keď sa objekt uvoľní | Nie |
| Kedy použiť? | Keď objekt môže byť nil |
Keď objekt nemôže byť nil |
| Výkon | Trochu pomalší kvôli kontrole nil |
Trochu rýchlejší |
Okrem toho, cykly uchovávania sa môžu vyskytnúť aj v uzávierkach (closures).
Keď uzávierka zachytí vlastnosti inštancie alebo self, môže vytvoriť silnú referenciu na objekt. Ak má tento objekt tiež silnú referenciu na uzávierku (napríklad cez vlastnosť-uzávierku alebo delegáta), vzniká cyklus uchovávania.
Ako vyriešiť problém cyklov uchovávania v uzávierkach:
Použite zoznam zachytenia (capture list).
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // zoznam zachytenia s weak self
guard let self = self else { return } // kontrola na `nil` pre weak self
self.value += 1
print("Hodnota je teraz \(self.value)")
}
}
}
V tomto príklade, [weak self] vytvára slabú referenciu na self vnútri uzávierky, čím sa predchádza cyklu uchovávania.