Sobes.tech
Middle+

Moet je altijd weak of unowned gebruiken in de Capture List?

sobes.tech AI

Antwoord van AI

Nee, niet altijd.

Weak en unowned worden gebruikt om sterke referentieringen (retention cycles) in closures te voorkomen, wanneer de closure een sterke referentie vasthoudt aan een object, en het object op zijn beurt een sterke referentie vasthoudt aan de closure.

Het is nodig om weak of unowned te gebruiken wanneer de closure een sterke referentie vasthoudt aan self of een ander referentietype, en er een kans op een cyclus van sterke referenties bestaat.

Situaties waarin weak/unowned niet nodig is:

  • De closure vangt waardetypes (structuren, enumeraties, tuples). Deze worden bij het vangen gekopieerd, en er ontstaan geen sterke referentieringen.
  • De closure vangt een referentietype, maar er bestaat geen twee-weg sterke referentieringscyclus. Bijvoorbeeld, als de closure lokaal wordt gemaakt binnen een methode en niet wordt opgeslagen in een object waarnaar wordt verwezen.
  • De closure heeft een korte levensduur en wordt uitgevoerd voordat het object waarnaar wordt verwezen wordt vrijgegeven. Bijvoorbeeld, een closure die wordt doorgegeven als Completion Handler en onmiddellijk wordt uitgevoerd.

Keuze tussen weak en unowned:

  • weak: wordt gebruikt wanneer het gevangen object op elk moment nil kan worden vóór het uitvoeren van de closure. De referentie is optioneel (ObjectType?). Er is een nil-controle nodig voordat het wordt gebruikt.
  • unowned: wordt gebruikt wanneer bekend is dat het gevangen object altijd zal bestaan op het moment van uitvoering van de closure. De referentie is niet optioneel (ObjectType). Er is geen nil-controle nodig. Het gebruik van unowned op een nil object leidt tot een crash (runtime error).
// Voorbeeld waar `weak`/`unowned` niet nodig is (vastleggen van structuur)
struct MyStruct {
    var value: Int
}

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

// Voorbeeld waar `weak`/`unowned` niet nodig is (lokaal closure)
class MyClass {
    func doSomething() {
        let localValue = 5
        let localClosure = {
            print(self.description) // Vangt self, maar wordt niet opgeslagen in MyClass
        }
        localClosure()
    }

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

let myObject = MyClass()
myObject.doSomething()

// Voorbeeld waar `weak` nodig is (potentieel nil)
class AsyncOperation {
    var completionHandler: (() -> Void)?

    func performOperation() {
        // Simulatie van asynchrone operatie
        DispatchQueue.global().async {
            print("Operation completed")
            self.completionHandler?() // optionele chaining
        }
    }

    deinit {
        print("AsyncOperation deinitialized")
    }
}

class ViewController {
    var op: AsyncOperation?

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

    deinit {
        print("ViewController deinitialized")
    }
}

// Voorbeeld waar `unowned` nodig is (garantisch aanwezig)
class Button {
    var action: (() -> Void)?

    func tap() {
        action?()
    }

    deinit {
        print("Button deinitialized")
    }
}

class ViewControllerWithButton {
    var button: Button

    init() {
        button = Button()
        button.action = { [unowned self] in // unowned self
            print("Button tapped in ViewControllerWithButton: \(self)")
        }
    }

    deinit {
        print("ViewControllerWithButton deinitialized")
    }
}