Jak funguje automatické řízení paměti (ARC) ve Swiftu?
sobes.tech AI
Odpověď od AI
ARC (Automatické počítání odkazů) ve Swiftu sleduje a spravuje využití paměti vašich aplikací. Automaticky uvolňuje paměť používanou instancemi tříd, když již nejsou potřeba, čímž předchází únikům paměti.
Při vytváření nové instance třídy ARC alokuje blok paměti pro uložení informací o této instanci. Když instance již není potřeba, ARC uvolní tuto paměť, aby mohla být použita pro jiné účely.
ARC funguje tak, že počítá počet silných odkazů (strong references) na každou instanci třídy. Silný odkaz je odkaz, který drží instanci v paměti. Když počet silných odkazů na instanci klesne na nulu, ARC uvolní s ní spojenou paměť.
Ve Swiftu existují tři typy odkazů, které ovlivňují ARC:
- Silné odkazy (Strong References): zvyšují čítač odkazů na instanci.
- Slabé odkazy (Weak References): nezvyšují čítač odkazů. Používají se k zabránění cyklům silných odkazů, když dvě instance třídy drží navzájem silné odkazy. Slabý odkaz se automaticky stává
nilpři deinitializaci odkazu na objekt. - Nepřidělené odkazy (Unowned References): také nezvyšují čítač odkazů. Používají se, když jste si jisti, že odkazovaná instance bude vždy existovat, dokud existuje aktuální instance. Nemohou být volitelné (
nil). Pokus o přístup k deinitializovanému objektu přes nepřidělený odkaz povede k chybě za běhu.
Příklad cyklu silných odkazů:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) je inicializován")
}
deinit {
print("\(name) je deinitializován")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) je inicializován")
}
deinit {
print("Apartment \(unit) je deinitializován")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Person není deinitializován
unit4A = nil // Apartment není deinitializován
Pro řešení cyklu použijte slabé nebo nepřidělené odkazy. V tomto případě by tenant v Apartment měl být slabý odkaz:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) je inicializován")
}
deinit {
print("\(name) je deinitializován")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Použití slabého odkazu
init(unit: String) {
self.unit = unit
print("Apartment \(unit) je inicializován")
}
deinit {
print("Apartment \(unit) je deinitializován")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // Person je deinitializován
unit4A = nil // Apartment je deinitializován
Volba mezi slabým a nepřiděleným odkazem závisí na vztahu mezi objekty:
- Slabý: Pokud jeden z objektů může být
nildříve, než se druhý deinitializuje (volitelná hodnota). - Nepřidělený: Pokud jeden z objektů vždy existuje, dokud druhý (nevolitelná hodnota).
ARC také spravuje paměť používanou uzávěry (closures), které zachycují instance tříd. Cyklus silných odkazů může vzniknout, když uzávěr zachytí instanci třídy a tato instance má také silnou odkaz na uzávěr. K tomu slouží seznamy zachycení (capture lists) v uzávěrech.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Bez seznamu zachycení, možný silný cyklus zachycení
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 deinitializován")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement není deinitializován
Řešení pomocí seznamu zachycení [weak self]:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Použití weak self
guard let self = self else { return "" } // Kontrola existence self
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 deinitializován")
}
}
var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement je deinitializován
Použití [unowned self] je možné, pokud jste si jisti, že instance třídy (self) nebude nil před provedením uzávěru.
ARC výrazně zjednodušuje správu paměti ve Swiftu ve srovnání s ruční správou, ale vyžaduje pochopení typů odkazů, aby se předešlo únikům paměti.