Šta su ciklusi zadržavanja (retain cycles) u iOS?
sobes.tech АИ
Одговор од АИ
Цикли zadržavanja nastaju kada dva ili više objekata imaju jake reference jedan na drugog, stvarajući "zatvoreni krug". Kao rezultat toga, brojač referenci svakog objekta nikada ne dostiže nulu i on ne može biti oslobođen od ARC (Automatic Reference Counting). To dovodi do curenja memorije, jer objekti ostaju u memoriji čak i nakon što više nisu u upotrebi.
Primer:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) se inicijalizuje")
}
deinit {
print("\(name) se oslobađa")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) se inicijalizuje")
}
deinit {
print("Apartment \(unit) se oslobađa")
}
}
U ovom primeru, ako Person ima jaku referencu na Apartment i Apartment ima jaku referencu na Person, prilikom pokušaja oslobađanja ovi objekti neće biti obrisani, jer njihovi brojači referenci ne dostižu nulu.
Kako rešiti problem cikličnih referenci:
Koristite slabe (weak) ili neposredne (unowned) reference.
weak: Koristi se kada je moguće da povezani objekat postaneniltokom životnog ciklusa drugog objekta. Referenca automatski postajenilkada se objekat na koji se odnosi oslobodi. To je opcionalno.unowned: Koristi se kada je poznato da povezani objekat ima isti ili duži životni vek od objekta koji na njega ukazuje. Ne može bitinil.
Ispravljeni primer sa weak:
Promenimo referencu tenant u klasi Apartment u slabu:
class Apartment {
let unit: String
weak var tenant: Person? // koristimo weak
init(unit: String) {
self.unit = unit
print("Apartment \(unit) se inicijalizuje")
}
deinit {
print("Apartment \(unit) se oslobađa")
}
}
Sada, kada se Person objekat oslobodi, referenca tenant u Apartment automatski postaje nil, prekinuvši ciklus zadržavanja i omogućavajući da i Apartment bude oslobođen.
Uporedba weak i unowned:
| Karakteristika | weak |
unowned |
|---|---|---|
| Tip reference | Opcionalni | Neopcionalni |
Može li biti nil? |
Da, kada se objekat oslobodi | Ne |
| Kada koristiti? | Kada objekat može postati nil |
Kada objekat ne može biti nil |
| Performanse | Malo sporije zbog provere nil |
Malo brže |
Takođe, ciklusi zadržavanja mogu nastati i u zatvaranjima (closures).
Kada zatvaranje uhvati svojstva instance ili self, može stvoriti jaku referencu na objekat. Ako taj objekat takođe ima jaku referencu na zatvaranje (npr. putem svojstva-zatvaranja ili delegata), nastaje ciklus zadržavanja.
Kako rešiti problem ciklusa zadržavanja u zatvaranjima:
Koristite listu za hvatanje (capture list).
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // lista za hvatanje sa weak self
guard let self = self else { return } // provera na `nil` za weak self
self.value += 1
print("Vrednost je sada \(self.value)")
}
}
}
U ovom primeru, [weak self] stvara slab referencu na self unutar zatvaranja, sprečavajući ciklus zadržavanja.