Sobes.tech
Middle

Kā darbojas automātiskās atmiņas pārvaldība (ARC) Swift?

sobes.tech AI

Atbilde no AI

ARC (Automātiskās atsauces skaitīšanas) sistēma Swift uzrauga un pārvalda jūsu lietojumprogrammu atmiņas izmantošanu. Tā automātiski atbrīvo klases piemēru izmantoto atmiņu, kad tie vairs nav nepieciešami, novēršot atmiņas noplūdes:

Veidojot jaunu klases piemēru, ARC piešķir atmiņas bloku šīs piemēra informācijas glabāšanai. Kad piemērs vairs nav nepieciešams, ARC atbrīvo šo atmiņu, lai to varētu izmantot citiem mērķiem:

ARC darbojas, skaitot katra klases piemēra stipro atsauču (strong references) skaitu. Stipra atsauce — tas ir atsauce, kas tur piemēru atmiņā. Kad stipro atsauču skaits sasniedz nulli, ARC atbrīvo šo atmiņu:

Swift sistēmā ir trīs atsauču tipi, kas ietekmē ARC:

  1. Stipras atsauces (Strong References): palielina atsauču skaitu piemēram.
  2. Vājas atsauces (Weak References): nepalielina atsauču skaitu. Tās tiek izmantotas, lai izvairītos no stipro atsauču cikliem, kad divi klases piemēri tur stipras atsauces viens uz otru. Vāja atsauce automātiski kļūst par nil, kad objekts, uz kuru tā norāda, tiek dēmonizēts:
  3. Nepieejamas atsauces (Unowned References): arī nepalielina atsauču skaitu. Tās tiek izmantotas, kad esat pārliecināts, ka atsauce uz objektu vienmēr pastāvēs, kamēr pastāvēs pašreizējais piemērs. Tās nevar būt opcionalas (nil). Mēģinājums piekļūt dēmonizētam objektam caur nepieejamu atsauci izraisīs kļūdu:

Piemērs stipro atsauču ciklam:

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

    init(name: String) {
        self.name = name
        print("\(name) tiek inicializēts")
    }

    deinit {
        print("\(name) tiek dēmonizēts")
    }
}

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

    init(unit: String) {
        self.unit = unit
        print("Dzīvoklis \(unit) tiek inicializēts")
    }

    deinit {
        print("Dzīvoklis \(unit) tiek dēmonizēts")
    }
}

var john: Person?
var unit4A: Apartment?

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

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

john = nil // Person netiek dēmonizēts
unit4A = nil // Apartment netiek dēmonizēts

Lai atrisinātu šo problēmu, izmantojiet vājas vai nepielūdzamas atsauces. Šajā gadījumā, tenant Apartment jābūt vājas atsauces:

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

    init(name: String) {
        self.name = name
        print("\(name) tiek inicializēts")
    }

    deinit {
        print("\(name) tiek dēmonizēts")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Vāja atsauce

    init(unit: String) {
        self.unit = unit
        print("Dzīvoklis \(unit) tiek inicializēts")
    }

    deinit {
        print("Dzīvoklis \(unit) tiek dēmonizēts")
    }
}

var john: Person?
var unit4A: Apartment?

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

john?.apartment = unit4A
unit4A?.tenant = john

john = nil // Person tiek dēmonizēts
unit4A = nil // Apartment tiek dēmonizēts

Izvēle starp vājo un nepielūdzamu atsauci ir atkarīga no objektu attiecības:

  • Vāja (Weak): ja viens objekts var kļūt par nil pirms otra objekta dēmonizācijas (opcijas vērtība).
  • Nepieejama (Unowned): ja viens objekts pastāvīgi pastāvēs, kamēr pastāvēs otrs (neopcijas vērtība).

ARC arī pārvalda atmiņu, ko izmanto slēdzieni (closures), kas iekļauj klases piemērus. Stipri cikli var rasties, kad slēdzis iekļauj klases piemēru, un šis piemērs arī ir stipri saistīts ar slēdzi. Lai to novērstu, izmanto ieķeršanas sarakstus (capture lists) slēdzienos:

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

    lazy var asHTML: () -> String = {
        // Bez ieķeršanas saraksta, iespējams stiprs cikls
        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) tiek dēmonizēts")
    }
}

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

paragraph = nil // HTMLElement netiek dēmonizēts

Lai to novērstu, izmanto ieķeršanas sarakstu [weak self]:

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

    lazy var asHTML: () -> String = { [weak self] in // Vāja atsauce
        guard let self = self else { return "" } // Pārbaude, vai `self` pastāv
        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) tiek dēmonizēts")
    }
}

var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil // AnHTMLElement tiek dēmonizēts

Izmantošana [unowned self] ir iespējama, ja esat pārliecināts, ka instances (self) nekad nenovērtēs nil pirms slēdža izpildes.

ARC ievērojami vienkāršo atmiņas pārvaldību Swift, salīdzinot ar manuālo pārvaldību, bet prasa saprast atsauces tipus, lai novērstu atmiņas noplūdes.