Sobes.tech
Middle

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 weak sunt î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