Kako funkcioniše automatsko upravljanje memorijom (ARC) u Swift-u?
sobes.tech АИ
Одговор од АИ
ARC (Automatsko brojanje referenci) u Swiftu prati i upravlja korišćenjem memorije vaših aplikacija. Automatski oslobađa memoriju koju koriste instance klasa kada više nisu potrebne, čime sprečava curenje memorije.
Prilikom kreiranja nove instance klase, ARC dodeljuje blok memorije za čuvanje informacija o toj instanci. Kada instanca više nije potrebna, ARC oslobađa tu memoriju, tako da može biti korišćena za druge svrhe.
ARC funkcioniše tako što broji broj jakih referenci (strong references) na svaku instancu klase. Jaka referenca je ona koja drži instancu u memoriji. Kada broj jakih referenci na instancu padne na nulu, ARC oslobađa povezanu memoriju.
U Swiftu postoje tri tipa referenci koje utiču na ARC:
- Jake reference (Strong References): povećavaju brojač referenci na instancu.
- Slabe reference (Weak References): ne povećavaju brojač referenci. Koriste se da bi se izbegli ciklusi jakih referenci, kada dve instance klase drže jake reference jedna na drugu. Slaba referenca automatski postaje
nilpri deinitializaciji objekta na koji ukazuje. - Nepovezane reference (Unowned References): takođe ne povećavaju brojač referenci. Koriste se kada ste sigurni da će referencirani objekat uvek postojati dok postoji trenutni objekat. Ne mogu biti opcionalne (
nil). Pokušaj pristupa deinitializovanom objektu putem nepovezane reference izazvaće grešku u izvršenju.
Primer ciklusa jakih referenci:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) se inicijalizuje")
}
deinit {
print("\(name) se deinitializuje")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Stan \(unit) se inicijalizuje")
}
deinit {
print("Stan \(unit) se deinitializuje")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Person se ne deinitializuje
unit4A = nil // Apartment se ne deinitializuje
Da bi se rešio ciklus, koristite slabe ili nepovezane reference. U ovom slučaju, tenant u Apartment treba biti slaba referenca:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) se inicijalizuje")
}
deinit {
print("\(name) se deinitializuje")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Korišćenje slabe reference
init(unit: String) {
self.unit = unit
print("Stan \(unit) se inicijalizuje")
}
deinit {
print("Stan \(unit) se deinitializuje")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // Person se deinitializuje
unit4A = nil // Apartment se deinitializuje
Izbor između slabe i nepovezane reference zavisi od odnosa između objekata:
- Slaba: Ako jedan od objekata može postati
nilpre nego što se drugi deinitializuje (opciona vrednost). - Nepovezana: Ako jedan od objekata uvek postoji dok postoji drugi (neopciona vrednost).
ARC takođe upravlja memorijom koju koriste zatvaranja (closures) koja hvata instance klasa. Ciklus jakih referenci može nastati kada zatvaranje hvata instancu klase, a ta klasa takođe ima jaku referencu na zatvaranje. Za sprečavanje toga koriste se liste hvatanja (capture lists) u zatvaranjima.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Bez liste hvatanja, mogući jak ciklus hvatanja
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) se deinitializuje")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement se ne deinitializuje
Rešenje korišćenjem liste hvatanja [weak self]:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Korišćenje weak self
guard let self = self else { return "" } // Provera postojanja 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) se deinitializuje")
}
}
var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement se deinitializuje
Korišćenje [unowned self] je moguće ako ste sigurni da instanca klase (self) neće postati nil pre izvršenja zatvaranja.
ARC znatno pojednostavljuje upravljanje memorijom u Swift-u u poređenju sa ručnim upravljanjem, ali zahteva razumevanje tipova referenci kako bi se sprečili curenje memorije.