Sobes.tech
Middle+

Vždy je třeba používat weak nebo unowned v Capture List?

sobes.tech AI

Odpověď od AI

Ne, ne vždy.

weak a unowned se používají k prevenci silových cyklů (retain cycles) v uzávěrech, kdy uzávěr drží silnou referenci na objekt a objekt zase drží silnou referenci na uzávěr.

weak nebo unowned je třeba použít, když uzávěr zachytí silnou referenci na self nebo jiný referenční typ a existuje pravděpodobnost vzniku silového cyklu.

Situace, kdy není třeba používat weak/unowned:

  • Uzávěr zachytí hodnotové typy (struktury, výčty, n-tice). Ty jsou při zachycení kopírovány a nevznikají cykly.
  • Uzávěr zachytí referenční typ, ale neexistuje obousměrný silný odkaz. Například pokud je uzávěr vytvořen lokálně uvnitř metody a není uložen objektem, na který odkazuje.
  • Uzávěr má krátkou životnost a bude vykonán dříve, než bude objekt, na který odkazuje, uvolněn. Například uzávěr předaný jako Completion Handler, který se ihned vykoná.

Volba mezi weak a unowned:

  • weak: používá se, když zachycený objekt může být kdykoli nil před vykonáním uzávěru. Odkaz je volitelný (ObjectType?). Před použitím je třeba zkontrolovat na nil.
  • unowned: používá se, když je známo, že zachycený objekt vždy bude existovat v době vykonání uzávěru. Odkaz není volitelný (ObjectType). Kontrola na nil není potřeba. Použití unowned u objektu, který se stal nil, povede k pádu (runtime error).
// Příklad, kdy není potřeba `weak`/`unowned` (zachycení struktury)
struct MyStruct {
    var value: Int
}

let myStruct = MyStruct(value: 10)
let closure = {
    print(myStruct.value) // kopie myStruct
}
closure()

// Příklad, kdy není potřeba `weak`/`unowned` (lokální uzávěr)
class MyClass {
    func doSomething() {
        let localValue = 5
        let localClosure = {
            print(self.description) // zachycuje self, ale uzávěr není uložen v MyClass
        }
        localClosure()
    }

    var description: String {
        return "Instance of MyClass"
    }
}

let myObject = MyClass()
myObject.doSomething()

// Příklad, kdy je potřeba `weak` (může být `nil`)
class AsyncOperation {
    var completionHandler: (() -> Void)?

    func performOperation() {
        DispatchQueue.global().async {
            print("Operace dokončena")
            self.completionHandler?()
        }
    }

    deinit {
        print("AsyncOperation uvolněn")
    }
}

class ViewController {
    var op: AsyncOperation?

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

    deinit {
        print("ViewController uvolněn")
    }
}

// Příklad, kdy je potřeba `unowned` (zaručeně existuje)
class Button {
    var action: (() -> Void)?

    func tap() {
        action?()
    }

    deinit {
        print("Button uvolněn")
    }
}

class ViewControllerWithButton {
    var button: Button

    init() {
        button = Button()
        button.action = { [unowned self] in
            print("Tlačítko stisknuto ve ViewControllerWithButton: \(self)")
        }
    }

    deinit {
        print("ViewControllerWithButton uvolněn")
    }
}