Sobes.tech
Middle

Kako funkcioniše mehanizam automatskog upravljanja memorijom (ARC) u jeziku Swift?

sobes.tech АИ

Одговор од АИ

ARC (Automatsko brojanje referenci) automatski prati i upravlja referencama na instance klasa. On oslobađa memoriju koju zauzima instanca kada na nju više nema jakih referenci. ARC radi samo sa instancama klasa, a ne sa strukturama ili enumeracijama, jer su one tipovi-vrednosti.

Tri tipa referenci između instanci:

  • Strong (Jaka): Referenca koja povećava brojač referenci na instanci. Podrazumevano, sve reference su jake.
  • Weak (Slaba): Referenca koja ne povećava brojač referenci. Koristi se za sprečavanje cikličnih jaza jakih referenci. weak reference su uvek opcionalne, jer instanca može biti oslobođena u bilo kom trenutku.
  • Unowned (Neposedovana): Referenca koja ne povećava brojač referenci i koristi se kada je garantovano da će povezani primerak postojati duže od trenutnog. Nije obavezno, ali pokušaj pristupa oslobođenom primerku izazvaće grešku u izvršenju.

Ciklični jaci odnosi nastaju kada dve ili više instanci klase imaju jake reference jedna na drugu, sprečavajući njihovo oslobađanje od strane ARC. Za rešavanje cikličnih odnosa koriste se weak ili unowned reference.

Primer cikličnog jaza:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
    }

    deinit { // poziva se pri oslobađanju
        print("\(name) se oslobađa")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
    }

    deinit { // poziva se pri oslobađanju
        print("Stan \(unit) se oslobađa")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // Ništa se ne ispisuje, jer postoji ciklični jaci odnosi
unit4A = nil // Ništa se ne ispisuje, jer postoji ciklični jaci odnosi

Rešenje cikličnog jaza korišćenjem weak:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\(name) se oslobađa")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Slaba referenca na Person

    init(unit: String) {
        self.unit = unit
    }

    deinit {
        print("Stan \(unit) se oslobađa")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // Oslobađa Person
unit4A = nil // Oslobađa Apartment

Rešenje cikličnog jaza korišćenjem unowned:

class Customer {
    let name: String
    var card: CreditCard?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\(name) se oslobađa")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Bezposedna referenca na Customer

    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }

    deinit {
        print("Karta #\(number) se oslobađa")
    }
}

var john: Customer?

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

john = nil // Oslobađa Customer i CreditCard

ARC u zatvaranjima: Zatvaranja takođe mogu stvoriti ciklične odnose, "hvatajući" instance klasa. Za sprečavanje toga koriste se liste hvatanja ([weak self] ili [unowned self]).

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = { // lazy za mogućnost korišćenja self pre inicijalizacije
        [unowned self] in // Lista hvatanja sa "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) se oslobađa")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "zdravo, svet")
print(paragraph!.asHTML())

paragraph = nil // HTMLElement se oslobađa