Kaip veikia automatinis atminties valdymas (ARC) Swift?
sobes.tech AI
Atsakymas iš AI
ARC (Automatinė nuorodų skaičiavimo sistema) Swift'e stebi ir valdo jūsų programų atminties naudojimą. Ji automatiškai atlaisvina klasių pavyzdžių naudojamą atmintį, kai jie nebėra reikalingi, taip išvengdama atminties nutekėjimo:
Kuriant naują klasės pavyzdį, ARC paskirsto atminties bloką šiai informacijai saugoti. Kai pavyzdys nebėra reikalingas, ARC atlaisvina tą atmintį, kad ją galėtų naudoti kitiems tikslams:
ARC veikia skaičiuodamas kiekvieno klasės pavyzdžio stipriųjų nuorodų (strong references) skaičių. Stipri nuoroda — tai nuoroda, kuri laiko pavyzdį atmintyje. Kai stipriųjų nuorodų skaičius pasiekia nulį, ARC atlaisvina tą atmintį:
Swift'e yra trys nuorodų tipai, kurie veikia ARC:
- Stipriosios nuorodos (Strong References): didina nuorodų skaičių pavyzdyje.
- Sunkiosios nuorodos (Weak References): nepadidina nuorodų skaičiaus. Naudojamos, kad išvengtų stipriųjų nuorodų ciklų, kai du klasės pavyzdžiai laiko stipriąsias nuorodas vienas į kitą. Sunkioji nuoroda automatiškai tampa
nil, kai objektas, į kurį ji nuoroda, yra dėsniuojamas: - Nepasiekiamos nuorodos (Unowned References): taip pat nepadidina nuorodų skaičiaus. Naudojamos, kai esate tikri, kad nuoroda į objektą visada egzistuos, kol egzistuos dabartinis pavyzdys. Negali būti pasirinktinė (
nil). Bandymas pasiekti dėsniuojamą objektą per nepastebimą nuorodą sukels klaidą:
Pavyzdys stipriųjų nuorodų ciklo:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) yra inicijuotas")
}
deinit {
print("\(name) yra dėsniuojamas")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Butas \(unit) yra inicijuotas")
}
deinit {
print("Butas \(unit) yra dėsniuojamas")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Person nėra dėsniuojamas
unit4A = nil // Apartment nėra dėsniuojamas
Sprendžiant problemą, naudokite silpną arba nepastebimą nuorodą. Šiuo atveju, tenant Apartment turi būti silpna nuoroda:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) yra inicijuotas")
}
deinit {
print("\(name) yra dėsniuojamas")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Silpna nuoroda
init(unit: String) {
self.unit = unit
print("Butas \(unit) yra inicijuotas")
}
deinit {
print("Butas \(unit) yra dėsniuojamas")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // Person yra dėsniuojamas
unit4A = nil // Apartment yra dėsniuojamas
Pasirinkimas tarp silpnos ir nepastebimos nuorodos priklauso nuo objektų santykio:
- Silpna (Weak): Jei vienas iš objektų gali tapti
niliki kito dėsniuojant (pasirinktinė reikšmė). - Nepastebima (Unowned): Jei vienas iš objektų visada egzistuos, kol egzistuos kitas (nepasirinktinė reikšmė).
ARC taip pat valdo atmintį, naudojamą uždarymuose (closures), kurie įtraukia klasių pavyzdžius. Stiprių ciklų gali atsirasti, kai uždarymas įtraukia klasės pavyzdį, ir šis pavyzdys taip pat turi stiprią nuorodą į uždarymą. Norint to išvengti, naudojami įrašų sąrašai (capture lists) uždarymuose:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Be įrašų sąrašo, gali būti stiprus ciklas
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) yra dėsniuojamas")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement nėra dėsniuojamas
Sprendimas naudojant įrašų sąrašą [weak self]:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Naudojama silpna nuoroda
guard let self = self else { return "" } // Patikrinimas, ar `self` egzistuoja
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) yra dėsniuojamas")
}
}
var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement yra dėsniuojamas
Naudojimas [unowned self] yra įmanomas, jei esate tikri, kad self nebus nil iki uždarymo vykdymo.
ARC žymiai supaprastino atminties valdymą Swift, palyginti su rankiniu valdymu, tačiau reikalauja suprasti nuorodų tipus, kad būtų išvengta atminties nutekėjimo.