Sobes.tech
Middle

Kas yra išlaikymo ciklai (retain cycles) iOS?

sobes.tech AI

Atsakymas iš AI

Ciklai išlaikymo susidaro, kai du ar daugiau objektų turi stiprius ryšius vienas su kitu, sudarydami "uždarą ratą". Dėl to kiekvieno objekto nuorodų skaitiklis niekada nepasiekia nulio, ir jis negali būti atlaisvintas ARC (Automatinis nuorodų skaičiavimas). Tai sukelia atminties nutekėjimus, nes objektai lieka atmintyje net ir tada, kai jie nebenaudojami.

Pavyzdys:

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

    init(name: String) {
        self.name = name
        print("\(name) yra inicijuojamas")
    }

    deinit {
        print("\(name) yra de-inicijuojamas")
    }
}

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

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) yra inicijuojamas")
    }

    deinit {
        print("Apartment \(unit) yra de-inicijuojamas")
    }
}

Šiame pavyzdyje, jei Person turi stiprią nuorodą į Apartment, o Apartment turi stiprią nuorodą į Person, tada bandant atlaisvinti šiuos objektus jie nebus pašalinti, nes jų nuorodų skaitikliai nepasieks nulio.

Kaip išspręsti ciklinius ryšius:

Naudokite silpnus (weak) arba nepriklausomus (unowned) ryšius.

  • weak: Naudojama, kai tikėtina, kad susijęs objektas taps nil per objekto gyvavimo ciklą. Nuoroda automatiškai tampa nil, kai objektas, į kurį ji nurodo, yra atlaisvinamas. Yra pasirinktinė.
  • unowned: Naudojama, kai žinoma, kad susijęs objektas turi tą patį arba ilgesnį gyvavimo ciklą nei objektas, turintis nuorodą į jį. Negali būti nil.

Pakeistas pavyzdys naudojant weak:

Pakeiskime tenant nuorodą Apartment klasėje į silpną:

class Apartment {
    let unit: String
    weak var tenant: Person? // Naudojame weak

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) yra inicijuojamas")
    }

    deinit {
        print("Apartment \(unit) yra de-inicijuojamas")
    }
}

Dabar, kai Person objektas atlaisvinamas, nuoroda tenant Apartment automatiškai tampa nil, nutraukdama ciklą ir leidžiant Apartment taip pat būti atlaisvintam.

Lyginamoji lentelė weak ir unowned:

Savybė weak unowned
Nuorodos tipas Pasirinktinė Pasirinktinė nėra
Gali būti nil? Taip, kai objektas atlaisvinamas Ne
Kada naudoti Kai objektas gali tapti nil Kai objektas negali būti nil
Veikimas Šiek tiek lėčiau dėl nil patikros Šiek tiek greičiau

Ciklinių ryšių gali kilti ir su užklausomis (closures).

Kai užklausa įtraukia self arba savybes, ji gali sukurti stiprią nuorodą į objektą. Jei šis objektas taip pat turi stiprią nuorodą į užklausą (pvz., per savybę arba delegatą), susidaro ciklas.

Kaip išspręsti ciklinius ryšius su užklausomis:

Naudokite įrašymo sąrašą (capture list).

class SomeClass {
    var value = 0

    func doSomethingDelayed() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // įrašymo sąrašas su weak self
            guard let self = self else { return } // `self` patikra
            self.value += 1
            print("Value is now \(self.value)")
        }
    }
}

Šiame pavyzdyje [weak self] sukuria silpną nuorodą į self užklausos viduje, išvengiant ciklinio ryšio.