Sobes.tech
Middle+

Vždy je potrebné použiť weak alebo unowned v Capture List?

sobes.tech AI

Odpoveď od AI

Nie, nie vždy.

weak a unowned sa používajú na prevenciu silových cyklov (retain cycles) v uzávierkach, keď uzávierka drží silnú referenciu na objekt a objekt zase drží silnú referenciu na uzávierku.

weak alebo unowned je potrebné použiť, ak uzávierka zachytí silnú referenciu na self alebo iný referenčný typ a existuje pravdepodobnosť vzniku silového cyklu.

Situácie, keď nie je potrebné používať weak/unowned:

  • Uzávierka zachytí hodnotové typy (štruktúry, enumy, tuple). Tie sa kopírujú pri zachytení a nevznikajú cykly silových referencií.
  • Uzávierka zachytí referenčný typ, ale neexistuje obojsmerný silný cyklus. Napríklad, ak je uzávierka vytvorená lokálne v metóde a nie je uložená objektom, na ktorý odkazuje.
  • Uzávierka má krátky život a bude vykonaná skôr, ako bude objekt, na ktorý odkazuje, uvoľnený. Napríklad, uzávierka odovzdaná ako Completion Handler, ktorá sa okamžite vykoná.

Voľba medzi weak a unowned:

  • weak: používa sa, keď zachytený objekt môže byť kedykoľvek nil pred vykonaním uzávierky. Odkaz je ako voliteľný (ObjectType?). Vyžaduje sa kontrola na nil pred použitím.
  • unowned: používa sa, keď je známe, že zachytený objekt vždy bude existovať v čase vykonania uzávierky. Odkaz nie je voliteľný (ObjectType). Kontrola na nil nie je potrebná. Použitie unowned u objektu, ktorý sa stal nil, povedie k pádu (runtime error).
// Príklad, keď nie je potrebné `weak`/`unowned` (zachytenie štruktúry)
struct MyStruct {
    var value: Int
}

let myStruct = MyStruct(value: 10)
let closure = {
    print(myStruct.value) // kopíruje myStruct
}
closure()

// Príklad, keď nie je potrebné `weak`/`unowned` (lokálne uzávier)
class MyClass {
    func doSomething() {
        let localValue = 5
        let localClosure = {
            print(self.description) // zachytáva self, ale uzávier sa neukladá v MyClass
        }
        localClosure()
    }

    var description: String {
        return "Inštancia MyClass"
    }
}

let myObject = MyClass()
myObject.doSomething()

// Príklad, keď je potrebné `weak` (môže byť `nil`)
class AsyncOperation {
    var completionHandler: (() -> Void)?

    func performOperation() {
        DispatchQueue.global().async {
            print("Operácia dokončená")
            self.completionHandler?()
        }
    }

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

class ViewController {
    var op: AsyncOperation?

    func setupOperation() {
        op = AsyncOperation()
        op?.completionHandler = { [weak self] in
            guard let self = self else { return }
            print("Completion handler vo ViewController: \(self)")
        }
    }

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

// Príklad, keď je potrebné `unowned` (zaručene existuje)
class Button {
    var action: (() -> Void)?

    func tap() {
        action?()
    }

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

class ViewControllerWithButton {
    var button: Button

    init() {
        button = Button()
        button.action = { [unowned self] in
            print("Tlačidlo stlačené vo ViewControllerWithButton: \(self)")
        }
    }

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