Kuidas töötab automaatne mäehaldussüsteem (ARC) Swiftis?
sobes.tech AI
Vastus AI-lt
ARC (Automaatne viitearvutamine) Swiftis jälgib ja haldab teie rakenduste mälu kasutust. See vabastab automaatselt klassi eksemplarite kasutatud mälu, kui need enam ei ole vajalikud, vältides mälu lekked:
Uue klassi eksemplari loomisel eraldab ARC mälu selle teabe salvestamiseks. Kui eksemplar ei ole enam vajalik, vabastab ARC selle mälu, et seda saaks kasutada muudel eesmärkidel:
ARC töötab, arvutades iga klassi eksemplari tugevate viidete (strong references) arvu. Tugev viide — see on viide, mis hoiab eksemplari mälus. Kui tugevate viidete arv jõuab nulli, vabastab ARC selle mälu:
Swiftis on kolm viiteliiki, mis mõjutavad ARC:
- Tugevad viited (Strong References): suurendavad viidete arvu eksemplaril.
- Nõrgad viited (Weak References): ei suurenda viidete arvu. Kasutatakse, et vältida tugevate viidete tsükleid, kui kaks klassi eksemplari hoiavad üksteise tugevaid viiteid. Nõrk viide muutub automaatselt
nil, kui sellele viitav objekt deinitialiseeritakse: - Unowned viited (Unowned References): samuti ei suurenda viidete arvu. Kasutatakse, kui olete kindel, et viidatav objekt eksisteerib alati, kuni eksisteerib praegune eksemplar. Need ei saa olla valikulised (
nil). Püüdes ligi pääseda deinitialiseeritud objekti unowned viite kaudu, tekib viga:
Näide tugevate viidete tsüklist:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) on initsialiseeritud")
}
deinit {
print("\(name) on deinitialiseeritud")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Korter \(unit) on initsialiseeritud")
}
deinit {
print("Korter \(unit) on deinitialiseeritud")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Person ei deinitialiseeru
unit4A = nil // Apartment ei deinitialiseeru
Probleemi lahendamiseks kasutage nõrku või unowned viiteid. Antud juhul peaks tenant Apartment-is olema nõrk viide:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) on initsialiseeritud")
}
deinit {
print("\(name) on deinitialiseeritud")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // nõrk viide
init(unit: String) {
self.unit = unit
print("Korter \(unit) on initsialiseeritud")
}
deinit {
print("Korter \(unit) on deinitialiseeritud")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // Person deinitialiseerub
unit4A = nil // Apartment deinitialiseerub
Valik nõrga ja unowned viite vahel sõltub objektide suhtest:
- Weak: Kui üks objekt võib muutuda
nilenne teise deinitialiseerimist (valikuline väärtus). - Unowned: Kui üks objekt eksisteerib alati, kuni teine eksisteerib (mitte-valikuline väärtus).
ARC haldab ka mälu, mida kasutavad sulgudes (closures), mis haaravad klassi eksemplare. Tugevad tsüklid võivad tekkida, kui sulg haarab klassi eksemplari ning see eksemplar omab tugevat viidet sulgudele. Selle vältimiseks kasutatakse haaramisnimekirju (capture lists) sulgudes:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Haaramisnimekirja pole, võimalik tugev tsükkel
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) on deinitialiseeritud")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement ei deinitialiseeru
Lahendus haaramisnimekirja [weak self] kasutamisega:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Weak self
guard let self = self else { return "" } // Kontroll, kas self eksisteerib
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) on deinitialiseeritud")
}
}
var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement deinitialiseerub
Kasutamine [unowned self] on võimalik, kui olete kindel, et eksemplar (self) ei muutu nil enne sulgude täitmist.
ARC märkimisväärselt lihtsustas mälu haldamist Swiftis võrreldes käsitsi haldamisega, kuid nõuab arusaamist viiteliikidest, et vältida mälu lekked.