Sobes.tech
Middle

Kaip veikia automatinės atminties valdymo (ARC) mechanizmas Swift kalboje?

sobes.tech AI

Atsakymas iš AI

ARC (Automatinis nuorodžių skaičiavimas) automatiškai stebi ir valdo nuorodas į klasių pavyzdžius. Jis atlaisvina atmintį, užimamą pavyzdžio, kai nėra jokių stiprių nuorodų į jį. ARC veikia tik su klasių pavyzdžiais, o ne su struktūromis ar išvardijimais, nes jie yra reikšmių tipai.

Tarp pavyzdžių yra trys ryšių tipai:

  • Strong (Stiprus): Ryšys, kuris padidina nuorodų skaitiklį pavyzdyje. Pagal numatytuosius nustatymus visi ryšiai yra stiprūs.
  • Weak (Silpnas): Ryšys, kuris nepadidina nuorodų skaitiklio. Naudojamas stiprių nuorodų ciklų prevencijai. weak tipo nuorodos visada yra pasirinktinės, nes pavyzdys gali būti atlaisvintas bet kuriuo metu.
  • Unowned (Nepavaldus): Ryšys, kuris nepadidina nuorodų skaitiklio ir naudojamas, kai garantuojama, kad susijęs pavyzdys egzistuos ilgiau nei dabartinis. Nebūtina, bet bandant pasiekti atlaisvintą pavyzdį, kyla vykdymo klaida.

Stiprių nuorodų ciklai atsiranda, kai du ar daugiau klasių pavyzdžių turi stiprius ryšius vienas su kitu, užkertant kelią jų atlaisvinimui ARC. Šių ciklų sprendimui naudojami weak arba unowned ryšiai.

Pavyzdys stiprių nuorodų ciklo:

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

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

    deinit { // išlaisvinimo metu
        print("\(name) yra išlaisvinamas")
    }
}

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

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

    deinit { // išlaisvinimo metu
        print("Butas \(unit) yra išlaisvinamas")
    }
}

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 // Nieko neatsitinka, nes susidarė ciklas

Sprendimas stiprių nuorodų ciklui naudojant weak:

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

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

    deinit {
        print("\(name) yra išlaisvinamas")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Silpna nuoroda į Person

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

    deinit {
        print("Butas \(unit) yra išlaisvinamas")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil // Išlaisvina Person
unit4A = nil // Išlaisvina Apartment

Sprendimas stiprių nuorodų ciklui naudojant unowned:

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

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

    deinit {
        print("\(name) yra išlaisvinamas")
    }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // Nepavaldus ryšys

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

    deinit {
        print("Kortelė #\(number) yra išlaisvinama")
    }
}

var john: Customer?

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

john = nil // Išlaisvina Customer ir CreditCard

ARC ir uždarymuose: Uždarymai taip pat gali sukelti ciklus, įtvirtindami klasių pavyzdžius. Norint to išvengti, naudojami [weak self] arba [unowned self] įrašai:

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

    lazy var asHTML: () -> String = { // lazy, kad galėtų naudoti self prieš inicializaciją
        [unowned self] in // įtvirtinimo sąrašas
        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) yra išlaisvinamas")
    }
}

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

paragraph = nil // Išlaisvina HTMLElement