Sobes.tech
Middle

Čo sú to cykly udržiavania (retain cycles) v iOS?

sobes.tech AI

Odpoveď od AI

Cyklusy zadržania vznikajú, keď dva alebo viac objektov majú silné odkazy na seba navzájom, čím vytvárajú "uzavretý kruh". V dôsledku toho počet odkazov na každý objekt nikdy nedosiahne nulu a nemôže byť uvoľnený ARC (Automatic Reference Counting). To vedie k únikom pamäte, pretože objekty zostávajú v pamäti aj po ukončení používania.

Príklad:

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

    init(name: String) {
        self.name = name
        print("\(name) sa inicializuje")
    }

    deinit {
        print("\(name) sa uvoľňuje")
    }
}

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

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) sa inicializuje")
    }

    deinit {
        print("Apartment \(unit) sa uvoľňuje")
    }
}

V tomto príklade, ak má Person silnú referenciu na Apartment a Apartment má silnú referenciu na Person, pri pokuse o uvoľnenie týchto objektov nebudú odstránené, pretože ich počítadlá odkazov nebudú dosahovať nulu.

Ako vyriešiť problém cyklov uchovávania:

Použite slabé (weak) alebo nepovinné (unowned) odkazy.

  • weak: Používa sa, keď je možné, že súvisiaci objekt sa stane nil počas životného cyklu iného objektu. Odkaz sa automaticky stane nil, keď sa objekt, na ktorý odkazuje, uvoľní. Je voliteľný.
  • unowned: Používa sa, keď je známe, že súvisiaci objekt má rovnaký alebo dlhší životný cyklus ako objekt, ktorý na neho odkazuje. Nemôže byť nil.

Opravený príklad s použitím weak:

Zmeníme odkaz tenant v triede Apartment na slabý:

class Apartment {
    let unit: String
    weak var tenant: Person? // používame weak

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) sa inicializuje")
    }

    deinit {
        print("Apartment \(unit) sa uvoľňuje")
    }
}

Teraz, keď sa Person objekt uvoľní, odkaz tenant v Apartment sa automaticky stane nil, čím sa preruší cyklus uchovávania a umožní aj uvoľnenie Apartment.

Porovnanie weak a unowned:

Charakteristika weak unowned
Typ odkazu Voliteľný Nevoliteľný
Môže byť nil? Áno, keď sa objekt uvoľní Nie
Kedy použiť? Keď objekt môže byť nil Keď objekt nemôže byť nil
Výkon Trochu pomalší kvôli kontrole nil Trochu rýchlejší

Okrem toho, cykly uchovávania sa môžu vyskytnúť aj v uzávierkach (closures).

Keď uzávierka zachytí vlastnosti inštancie alebo self, môže vytvoriť silnú referenciu na objekt. Ak má tento objekt tiež silnú referenciu na uzávierku (napríklad cez vlastnosť-uzávierku alebo delegáta), vzniká cyklus uchovávania.

Ako vyriešiť problém cyklov uchovávania v uzávierkach:

Použite zoznam zachytenia (capture list).

class SomeClass {
    var value = 0

    func doSomethingDelayed() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // zoznam zachytenia s weak self
            guard let self = self else { return } // kontrola na `nil` pre weak self
            self.value += 1
            print("Hodnota je teraz \(self.value)")
        }
    }
}

V tomto príklade, [weak self] vytvára slabú referenciu na self vnútri uzávierky, čím sa predchádza cyklu uchovávania.