Sobes.tech
Middle

Papaskinkite apie atminties valdymą iOS.

sobes.tech AI

Atsakymas iš AI

iOS naudoja automatinį atminties valdymą per ARC (Automatic Reference Counting). ARC automatiškai atlaisvina objektus, kai jiems nebėra nuorodų:

Pagrindinės sąvokos:

  • Stipri nuoroda: Padidina nuorodų skaitiklį objekto. Objekto nebus atlaisvinta, kol yra stiprių nuorodų.
  • Silpna nuoroda: Nepadidina nuorodų skaitiklio. Naudojama stiprių nuorodų ciklų prevencijai. Nuoroda į objektą, kuris gali būti atlaisvintas. Jei objektas atlaisvinamas, silpna nuoroda tampa nil.
  • Nepakeičiama nuoroda: Panaši į silpną, bet nepasidaro nil objekto atlaisvinimo metu. Naudojama, kai objektas turi tą patį arba ilgesnį gyvavimo ciklą. Nepakeičiama nuoroda nepasidaro nil atlaisvinus objektą. Bandant pasiekti atlaisvintą objektą per nepakeičiamą nuorodą, įvyksta klaida.

Stiprių nuorodų ciklai (Retain Cycles): atsiranda, kai du ar daugiau objektų turi stiprių nuorodų vienas į kitą, neleidžiančių jiems būti atlaisvintiems. Sprendžiama naudojant silpnus arba nepakeičiamus nuorodų tipus.

Pavyzdys:

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

    init(name: String) {
        self.name = name
        print("\(name) pradeda kurti")
    }

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

class Apartment {
    let unit: String
    weak var tenant: Person? // Naudojama silpna nuoroda, kad išvengtume ciklo

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) pradeda kurti")
    }

    deinit {
        print("Apartment \(unit) baigia")
    }
}

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

john!.apartment = unit4A
unit4A!.tenant = john // Čia susidaro ciklas

john = nil
unit4A = nil
// Deinitializacijos metodai neiškviečiami dėl ciklo

Sprendimas naudojant weak:

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

    init(name: String) {
        self.name = name
        print("\(name) pradeda kurti")
    }

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

class Apartment {
    let unit: String
    weak var tenant: Person? // Naudojama silpna nuoroda, kad išvengtume ciklo

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) pradeda kurti")
    }

    deinit {
        print("Apartment \(unit) baigia")
    }
}

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

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

john = nil
unit4A = nil
// Abu objektai teisingai išvalomi

Nepakeičiama vs Silpna:

  • Naudok weak, kai nuoroda gali tapti nil (objektas gali būti pirmas atlaisvintas)
  • Naudok unowned, kai nuoroda visada turės reikšmę (objektas, į kurį nuoroda, turi tą patį arba ilgesnį gyvavimo ciklą)

Uždarymai (Closures) ir ciklai:

Uždarymai gali įsikibti į objektų nuorodas, sukurdami stiprius ciklus. Tai sprendžiama naudojant capture list.

Pavyzdys:

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

    lazy var asHTML: () -> String = {
        [weak self] in // įkibti į `self` silpna nuoroda
        guard let self = self else {
            return ""
        }
        var html = "<\(self.name)"
        if let text = self.text {
            html += ">\(text)</\(self.name)>"
        } else {
            html += " />"
        }
        return html
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

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

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

paragraph = nil
// Objektas išvalytas

Atminties problemų derinimas:

  • Memory Graph Debugger: Xcode leidžia vizualizuoti objektų ryšius ir aptikti ciklus
  • Instruments (Allocations, Leaks): Stebi atminties paskirstymą ir išlaisvinimą, ieško nutekėjimų.