Middle
Vorbește-ne despre gestionarea memoriei în iOS.
sobes.tech AI
Răspuns de la AI
iOS utilizează referința automată (ARC) pentru gestionarea memoriei. ARC eliberează automat obiectele imediat ce nu mai există referințe către ele.
Concepte principale:
- Referință puternică: Crește contorul de referințe al unui obiect. Obiectul nu va fi eliberat atâta timp cât există referințe puternice către el.
- Referință slabă: Nu crește contorul de referințe. Este folosită pentru a preveni ciclurile de referințe puternice. Se referă la un obiect care poate fi eliberat. Dacă obiectul este eliberat, referința slabă devine
nil. - Referință neînțeleasă (unowned): Similar cu slabă, nu crește contorul de referințe. Este folosită atunci când obiectul la care se face referire are aceeași sau o durată de viață mai lungă. Referința neînțeleasă nu devine
nilla eliberarea obiectului. Accesarea unui obiect eliberat prin referința neînțeleasă duce la o eroare de execuție.
Cicluri de referințe puternice (Retain Cycles): Apare atunci când două sau mai multe obiecte au referințe puternice unul către celălalt, împiedicând eliberarea lor. Se rezolvă cu referințe slabă sau neînțeleasă.
Exemplu de ciclu de referințe puternice și soluția sa:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) este inițializat")
}
deinit {
print("\(name) este dezinitializat")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Folosește referința slabă pentru a preveni ciclul de referințe
init(unit: String) {
self.unit = unit
print("Apartament \(unit) este inițializat")
}
deinit {
print("Apartament \(unit) este dezinitializat")
}
}
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john // Aici apare ciclul de referințe
john = nil
unit4A = nil
// Metodele de dezinitializare nu sunt apelate din cauza ciclului de referințe
Soluție cu weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) este inițializat")
}
deinit {
print("\(name) este dezinitializat")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Folosește referința slabă pentru a preveni ciclul
init(unit: String) {
self.unit = unit
print("Apartament \(unit) este inițializat")
}
deinit {
print("Apartament \(unit) este dezinitializat")
}
}
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil
// Ambele obiecte sunt dezinitializate corect
Diferența între unowned și weak:
- Folosește
weakcând referința poate deveninil(obiectul poate fi eliberat primul). - Folosește
unownedcând referința va avea întotdeauna o valoare (obiectul la care se face referire are aceeași sau o durată de viață mai lungă).
Closures și cicluri de referințe:
Closures pot captura referințe la obiecte, creând cicluri de referințe puternice. Se rezolvă cu o listă de captură (capture list).
Exemplu:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[weak self] in // Capturează `self` slab
guard let self = self else {
return ""
}
var html = "<\(self.name)"
if let text = self.text {
html += ">\(text)</\(self.name)>"
} else {
html += " />"
}
return html
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) este dezinitializat")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil
// Obiectul este dezinitializat
Debugging memorie:
- Memory Graph Debugger: În Xcode, vizualizează conexiunile dintre obiecte și identifică ciclurile de referințe puternice.
- Instruments (Allocations, Leaks): Instrumente pentru monitorizarea alocării și eliberării memoriei, și detectarea scurgerilor.