Middle+
Czy zawsze trzeba używać weak lub unowned w liście przechwytywania?
sobes.tech AI
Odpowiedź od AI
Nie, nie zawsze.
Weak i unowned są używane do zapobiegania cyklom silnych referencji (retention cycles) w zamknięciach, gdy zamknięcie utrzymuje silną referencję do obiektu, a obiekt z kolei utrzymuje silną referencję do zamknięcia.
Konieczne jest użycie weak lub unowned, gdy zamknięcie przechwytuje silną referencję do self lub innego typu referencyjnego, i istnieje ryzyko powstania cyklu silnych referencji.
Sytuacje, gdy nie trzeba używać weak/unowned:
- Zamknięcie przechwytuje typy wartości (struktury, wyliczenia, krotki). Są one kopiowane podczas przechwytywania, i nie powstają cykle silnych referencji.
- Zamknięcie przechwytuje typ referencyjny, ale nie istnieje dwukierunkowy cykl silnych referencji. Na przykład, jeśli zamknięcie jest tworzone lokalnie w metodzie i nie jest przechowywane w obiekcie, na który się odwołuje.
- Zamknięcie ma krótkie życie i zostanie wykonane zanim obiekt, na który się odwołuje, zostanie zwolniony. Na przykład, zamknięcie przekazane jako obsługa zakończenia, które jest wykonywane natychmiast.
Wybór między weak a unowned:
weak: używane, gdy przechwycony obiekt może stać sięnilw dowolnym momencie przed wykonaniem zamknięcia. Referencja jest opcjonalna (ObjectType?). Wymaga sprawdzenia nanilprzed użyciem.unowned: używane, gdy wiadomo, że przechwycony obiekt zawsze będzie istniał w momencie wykonania zamknięcia. Referencja nie jest opcjonalna (ObjectType). Nie wymaga sprawdzania nanil. Użycieunowneddla obiektu, który stał sięnil, spowoduje błąd w czasie działania.
// Przykład, gdy nie jest potrzebne `weak`/`unowned` (przechwytywanie struktury)
struct MyStruct {
var value: Int
}
let myStruct = MyStruct(value: 10)
let closure = {
print(myStruct.value) // Kopiuje myStruct
}
closure()
// Przykład, gdy nie jest potrzebne `weak`/`unowned` (lokalne zamknięcie)
class MyClass {
func doSomething() {
let localValue = 5
let localClosure = {
print(self.description) // Przechwytuje self, ale nie jest przechowywane w MyClass
}
localClosure()
}
var description: String {
return "Instance of MyClass"
}
}
let myObject = MyClass()
myObject.doSomething()
// Przykład, gdy potrzebne jest `weak` (potencjalnie nil)
class AsyncOperation {
var completionHandler: (() -> Void)?
func performOperation() {
// Symulacja pracy asynchronicznej
DispatchQueue.global().async {
print("Operation completed")
self.completionHandler?() // łańcuch opcjonalny
}
}
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")
}
}
// Przykład, gdy potrzebne jest `unowned` (gwarantowane istnienie)
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")
}
}