Sobes.tech
Middle

Hogyan működik az automatik memória kezelés (ARC) mechanizmusa a Swift nyelvben?

sobes.tech MI

Válasz az MI-től

Az ARC (Automatikus Referenciaszámolás) automatikusan követi és kezeli a példányokra mutató hivatkozásokat. Felszabadítja a memóriát, amelyet egy példány foglal el, amikor már nincs rá erős hivatkozás. Az ARC csak osztálypéldányokkal működik, nem struktúrákkal vagy felsorolásokkal, mivel ezek értéktípusok.

Három típusú hivatkozás az példányok között:

  • Strong (Erős): Egy hivatkozás, amely növeli az példány hivatkozásszámát. Alapértelmezés szerint minden hivatkozás erős.
  • Weak (Gyenge): Egy hivatkozás, amely nem növeli a hivatkozásszámot. Használják az erős ciklusok elkerülésére. A weak típusú hivatkozások mindig opcionálisak, mivel az példány bármikor felszabadulhat.
  • Unowned (Független): Egy hivatkozás, amely nem növeli a hivatkozásszámot, és akkor használják, amikor garantált, hogy a kapcsolódó példány hosszabb ideig fog létezni, mint a jelenlegi. Nem kötelező, de a felszabadított példányhoz való hozzáférés futásidejű hibát okoz.

Az erős ciklusok akkor fordulnak elő, amikor két vagy több osztálypéldány erős hivatkozásokat tart egymásra, megakadályozva azok felszabadulását az ARC által. Ezek megoldására weak vagy unowned hivatkozásokat használnak.

Példa erős ciklusra:

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

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

    deinit { // felszabaduláskor hívódik
        print("\(name) felszabadul")
    }
}

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

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

    deinit { // felszabaduláskor hívódik
        print("Lakás \(unit) felszabadul")
    }
}

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 // Semmi nem jelenik meg, mert erős ciklus van

Az erős ciklusok megoldása weak használatával:

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

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

    deinit {
        print("\(name) felszabadul")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Gyenge hivatkozás

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

    deinit {
        print("Lakás \(unit) felszabadul")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil // Felszabadítja a Person-t
unit4A = nil // Felszabadítja a Lakást

Az erős ciklusok megoldása unowned használatával:

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

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

    deinit {
        print("\(name) felszabadul")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Független hivatkozás

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

    deinit {
        print("Kártya #\(number) felszabadul")
    }
}

var john: Customer?

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

john = nil // Felszabadítja a Customer-t és a CreditCard-t

ARC closure-ökben: A closure-k is képesek erős ciklusokat létrehozni, mivel példányokat foghatnak el. Ezt megelőzendő, a [weak self] vagy [unowned self] használatos.

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

    lazy var asHTML: () -> String = { // lazy, hogy self-t használhasson az inicializálás előtt
        [unowned self] in // Elfogási lista "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) felszabadul")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "helló, világ")
print(paragraph!.asHTML())

paragraph = nil // HTMLElement felszabadul