Sobes.tech
Middle

Vždy je potrebné použiť weak alebo unowned v zozname zachytenia?

sobes.tech AI

Odpoveď od AI

Nie, nie vždy.

Používame weak alebo unowned v Capture List na prevenciu cyklov udržiavania.

Cyklus udržiavania vzniká, keď si dva alebo viac objektov silno odkazujú, čím bránia uvoľneniu pamäte.

Použitie weak alebo unowned je potrebné, keď uzávierka silno odkazuje na self (alebo inú premennú inštancie triedy/aktora), a naopak self (alebo iný objekt) má silnú referenciu na túto uzávierku.

Príklady situácií, keď je to potrebné:

  • Asynchrónne operácie (sieťové požiadavky, spracovanie dát)
  • Observer vzor
  • Animačné bloky
  • Dispatch fronty

Ak uzávierka nezachytáva self (alebo inú premennú triedy/aktora, ktorá má silnú referenciu na uzávierku), alebo zachytáva štruktúrne typy/enumerácie, použitie weak alebo unowned nie je potrebné.

Rozdiel medzi weak a unowned:

Typ Popis Kedy použiť
weak Slabá (voliteľná) referencia. Môže sa stať nil. Keď je životný cyklus uzávierky kratší alebo rovný životnému cyklu zachyteného objektu, a objekt môže byť nil pred dokončením uzávierky.
unowned Nezávislá (nevoliteľná) referencia. Nemôže sa stať nil. Keď je životný cyklus uzávierky kratší alebo rovný životnému cyklu zachyteného objektu, a garantovane že objekt bude existovať do dokončenia uzávierky. Ak objekt odíde skôr, dôjde k pádu (unowned referencia na deallocovanú inštanciu).

Príklad použitia weak v Capture List:

class MyClass {
    var name: String = "Test"

    func performAsyncOperation() {
        DispatchQueue.global().async { [weak self] in
            guard let self = self else {
                return
            }
            print("Operácia dokončená pre \(self.name)")
        }
    }

    deinit {
        print("MyClass uvoľnený")
    }
}

Príklad použitia unowned v Capture List:

class ViewController {
    var dismissButton: UIButton!

    func setupButton() {
        dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
        // Tento príklad ukazuje, že `self` (ViewController) žije dlhšie alebo rovnako ako akcia tlačidla.
        // Target-action nie je uzávierka, ale ilustruje koncept unowned pri silnom cykle udržiavania.
        // Ak by to bola uzávierka, napr. v handleri UIControl.Event,
        // a ViewController je garantovane prítomný počas stlačenia tlačidla:
        // dismissButton.addAction(UIAction { [unowned self] _ in
        //     self.dismiss(animated: true, completion: nil)
        // }, for: .touchUpInside)
    }

    @objc func dismiss() {
        // Zatvorenie view controlleru
    }
}

Ak uzávierka nevytvára cyklus udržiavania, použitie weak alebo unowned nie je potrebné a môže dokonca viesť k zbytočným kontrolám (v prípade weak) alebo potenciálnym pádom (v prípade unowned).

Napríklad uzávierka, ktorá nezachytáva self alebo iný objekt, na ktorý má uzávierka silnú referenciu:

func fetchData(completion: @escaping (Data) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
        guard let data = data else { return }
        // Tu nie je zachytenie `self` alebo iných objektov, ktoré by mohli spôsobiť cyklus udržiavania.
        completion(data)
    }.resume()
}

V tomto prípade [weak self] alebo [unowned self] v Capture List nie sú potrebné.