Kā darbojas automātiskās atmiņas pārvaldība (ARC) Swift?
sobes.tech AI
Atbilde no AI
ARC (Automātiskās atsauces skaitīšanas) sistēma Swift uzrauga un pārvalda jūsu lietojumprogrammu atmiņas izmantošanu. Tā automātiski atbrīvo klases piemēru izmantoto atmiņu, kad tie vairs nav nepieciešami, novēršot atmiņas noplūdes:
Veidojot jaunu klases piemēru, ARC piešķir atmiņas bloku šīs piemēra informācijas glabāšanai. Kad piemērs vairs nav nepieciešams, ARC atbrīvo šo atmiņu, lai to varētu izmantot citiem mērķiem:
ARC darbojas, skaitot katra klases piemēra stipro atsauču (strong references) skaitu. Stipra atsauce — tas ir atsauce, kas tur piemēru atmiņā. Kad stipro atsauču skaits sasniedz nulli, ARC atbrīvo šo atmiņu:
Swift sistēmā ir trīs atsauču tipi, kas ietekmē ARC:
- Stipras atsauces (Strong References): palielina atsauču skaitu piemēram.
- Vājas atsauces (Weak References): nepalielina atsauču skaitu. Tās tiek izmantotas, lai izvairītos no stipro atsauču cikliem, kad divi klases piemēri tur stipras atsauces viens uz otru. Vāja atsauce automātiski kļūst par
nil, kad objekts, uz kuru tā norāda, tiek dēmonizēts: - Nepieejamas atsauces (Unowned References): arī nepalielina atsauču skaitu. Tās tiek izmantotas, kad esat pārliecināts, ka atsauce uz objektu vienmēr pastāvēs, kamēr pastāvēs pašreizējais piemērs. Tās nevar būt opcionalas (
nil). Mēģinājums piekļūt dēmonizētam objektam caur nepieejamu atsauci izraisīs kļūdu:
Piemērs stipro atsauču ciklam:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) tiek inicializēts")
}
deinit {
print("\(name) tiek dēmonizēts")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Dzīvoklis \(unit) tiek inicializēts")
}
deinit {
print("Dzīvoklis \(unit) tiek dēmonizēts")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Person netiek dēmonizēts
unit4A = nil // Apartment netiek dēmonizēts
Lai atrisinātu šo problēmu, izmantojiet vājas vai nepielūdzamas atsauces. Šajā gadījumā, tenant Apartment jābūt vājas atsauces:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) tiek inicializēts")
}
deinit {
print("\(name) tiek dēmonizēts")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Vāja atsauce
init(unit: String) {
self.unit = unit
print("Dzīvoklis \(unit) tiek inicializēts")
}
deinit {
print("Dzīvoklis \(unit) tiek dēmonizēts")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // Person tiek dēmonizēts
unit4A = nil // Apartment tiek dēmonizēts
Izvēle starp vājo un nepielūdzamu atsauci ir atkarīga no objektu attiecības:
- Vāja (Weak): ja viens objekts var kļūt par
nilpirms otra objekta dēmonizācijas (opcijas vērtība). - Nepieejama (Unowned): ja viens objekts pastāvīgi pastāvēs, kamēr pastāvēs otrs (neopcijas vērtība).
ARC arī pārvalda atmiņu, ko izmanto slēdzieni (closures), kas iekļauj klases piemērus. Stipri cikli var rasties, kad slēdzis iekļauj klases piemēru, un šis piemērs arī ir stipri saistīts ar slēdzi. Lai to novērstu, izmanto ieķeršanas sarakstus (capture lists) slēdzienos:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Bez ieķeršanas saraksta, iespējams stiprs cikls
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) tiek dēmonizēts")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement netiek dēmonizēts
Lai to novērstu, izmanto ieķeršanas sarakstu [weak self]:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Vāja atsauce
guard let self = self else { return "" } // Pārbaude, vai `self` pastāv
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) tiek dēmonizēts")
}
}
var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement tiek dēmonizēts
Izmantošana [unowned self] ir iespējama, ja esat pārliecināts, ka instances (self) nekad nenovērtēs nil pirms slēdža izpildes.
ARC ievērojami vienkāršo atmiņas pārvaldību Swift, salīdzinot ar manuālo pārvaldību, bet prasa saprast atsauces tipus, lai novērstu atmiņas noplūdes.