Ako funguje automatické riadenie pamäte (ARC) v Swift?
sobes.tech AI
Odpoveď od AI
ARC (Automatické počítanie referencií) vo Swiftu sleduje a spravuje využívanie pamäte vašich aplikácií. Automaticky uvoľňuje pamäť, ktorú používajú inštancie tried, keď už nie sú potrebné, čím zabraňuje únikom pamäte.
Pri vytváraní novej inštancie triedy ARC pridelení blok pamäte na uloženie informácií o tejto inštancii. Keď inštancia už nie je potrebná, ARC uvoľní túto pamäť, aby mohla byť použitá na iné účely.
ARC funguje tak, že počíta počet silných odkazov (strong references) na každú inštanciu triedy. Silný odkaz je odkaz, ktorý drží inštanciu v pamäti. Keď počet silných odkazov na inštanciu klesne na nulu, ARC uvoľní s ňou spojenú pamäť.
Vo Swift existujú tri typy odkazov, ktoré ovplyvňujú ARC:
- Silné odkazy (Strong References): zvyšujú čítač odkazov na inštanciu.
- Slabé odkazy (Weak References): neovplyvňujú čítač odkazov. Používajú sa na zabránenie cyklov silných odkazov, keď dve inštancie triedy držia navzájom silné odkazy. Slabý odkaz sa automaticky stáva
nilpri deinitializácii objektu, na ktorý ukazuje. - Nepovolené odkazy (Unowned References): tiež neovplyvňujú čítač odkazov. Používajú sa, keď ste si istí, že odkazovaný objekt bude vždy existovať, kým existuje aktuálny objekt. Nemôžu byť voliteľné (
nil). Pokus o prístup k deinitializovanému objektu cez nepovolený odkaz spôsobí chybu za behu.
Príklad cyklu silných odkazov:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) je inicializovaný")
}
deinit {
print("\(name) je deinitializovaný")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Byt \(unit) je inicializovaný")
}
deinit {
print("Byt \(unit) je deinitializovaný")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Person sa ne deinitializuje
unit4A = nil // Apartment sa ne deinitializuje
Na vyriešenie cyklu použite slabé alebo nepovolené odkazy. V tomto prípade by tenant v Apartment mal byť slabý odkaz:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) je inicializovaný")
}
deinit {
print("\(name) je deinitializovaný")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Použitie slabého odkazu
init(unit: String) {
self.unit = unit
print("Byt \(unit) je inicializovaný")
}
deinit {
print("Byt \(unit) je deinitializovaný")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // Person sa deinitializuje
unit4A = nil // Apartment sa deinitializuje
Voľba medzi slabým a nepovoleným odkazom závisí od vzťahu medzi objektmi:
- Slabý: Ak jeden z objektov môže byť
nilskôr, než sa druhý deinitializuje (opcionálna hodnota). - Nepovolený: Ak jeden z objektov vždy existuje, kým existuje druhý (neopcionálna hodnota).
ARC tiež spravuje pamäť používanú uzávierkami (closures), ktoré zachytávajú inštancie tried. Cyklus silných odkazov môže vzniknúť, keď uzávierka zachytí inštanciu triedy a táto inštancia má tiež silnú referenciu na uzávierku. Na zabránenie toho sa používajú zoznamy zachytenia (capture lists) v uzávierkach.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Bez zoznamu zachytenia, možný silný cyklus zachytenia
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name)>"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) je deinitializovaný")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement sa ne deinitializuje
Riešenie pomocou zoznamu zachytenia [weak self]:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Použitie weak self
guard let self = self else { return "" } // Kontrola, že self existuje
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name)>"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) je deinitializovaný")
}
}
var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement sa deinitializuje
Použitie [unowned self] je možné, ak ste si istí, že inštancia triedy (self) nebude nil pred vykonaním uzávierky.
ARC výrazne zjednodušuje správu pamäti vo Swift v porovnaní s ručnou správou, ale vyžaduje pochopenie typov odkazov na zabránenie únikom pamäti.