Cum funcționează mecanismul de gestionare automată a memoriei (ARC) în limbajul Swift?
sobes.tech AI
Răspuns de la AI
ARC (Numărare Automată a Referințelor) urmărește și gestionează automat referințele către instanțele de clase. El eliberează memoria ocupată de o instanță atunci când nu mai există referințe puternice către ea. ARC funcționează doar cu instanțe de clase, nu cu structuri sau enumerări, deoarece acestea sunt tipuri-valoare.
Trei tipuri de referințe între instanțe:
- Strong (Puternic): O referință care crește contorul de referințe al unei instanțe. În mod implicit, toate referințele sunt puternice.
- Weak (Slab): O referință care nu crește contorul de referințe. Este utilizată pentru a preveni ciclurile de referințe puternice. Referințele de tip
weaksunt întotdeauna opționale, deoarece instanța poate fi eliberată în orice moment. - Unowned (Fără proprietar): O referință care nu crește contorul de referințe și este utilizată atunci când se garantează că instanța asociată va exista mai mult timp decât cea curentă. Nu este obligatorie, dar încercarea de a accesa o instanță eliberată va cauza o eroare de execuție.
Ciclurile de referințe puternice apar atunci când două sau mai multe instanțe de clasă au referințe puternice unele către altele, împiedicând eliberarea lor de ARC. Pentru rezolvarea acestor cicluri, se folosesc referințe weak sau unowned.
Exemplu de ciclu de referințe puternice:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // este apelat la eliberare
print("\(name) este fiind dezinitializat")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // este apelat la eliberare
print("Apartament \(unit) este fiind dezinitializat")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil // Nu se afișează nimic, deoarece s-a creat un ciclu de referințe puternice
Rezolvarea ciclului de referințe puternice folosind weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) este fiind dezinitializat")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Referință slabă către Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartament \(unit) este fiind dezinitializat")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Eliberează Person
unit4A = nil // Eliberează Apartment
Rezolvarea ciclului de referințe puternice folosind unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) este fiind dezinitializat")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Referință fără proprietar către Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) este fiind dezinitializat")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Eliberează Customer și CreditCard
ARC în closures: Closures pot crea, de asemenea, cicluri de referințe puternice, captând instanțe de clase. Pentru a preveni acest lucru, se folosesc liste de captură ([weak self] sau [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy pentru a putea folosi self înainte de inițializare
[unowned self] in // Listă de captură cu "unowned 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) este fiind dezinitializat")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // Eliberează HTMLElement