Muss man immer weak oder unowned in der Capture-Liste verwenden?
sobes.tech KI
Antwort von AI
Nein, nicht immer.
Wir verwenden weak oder unowned in der Capture-Liste, um Retain Cycles zu verhindern.
Ein Retain Cycle tritt auf, wenn zwei oder mehr Objekte sich gegenseitig stark referenzieren, was verhindert, dass der Speicher freigegeben wird.
weak oder unowned sollten verwendet werden, wenn eine Closure stark auf self (oder eine andere Instanzvariable einer Klasse/Actors) verweist, und self (oder ein anderes Objekt) eine starke Referenz auf diese Closure hat.
Beispiele für Situationen, in denen dies notwendig ist:
- Asynchrone Operationen (Netzwerkanfragen, Datenverarbeitung)
- Observer-Muster
- Animationsblöcke
- Dispatch-Queues
Wenn die Closure self (oder eine andere Variable der Klasse/des Actors, die eine starke Referenz auf die Closure hat) nicht erfasst, oder strukturelle Typen/Enums erfasst, ist die Verwendung von weak oder unowned nicht erforderlich.
Unterschiede zwischen weak und unowned:
| Typ | Beschreibung | Wann verwenden |
|---|---|---|
weak |
Schwache (optionale) Referenz. Kann nil werden. |
Wenn der Lebenszyklus der Closure kürzer oder gleich dem des erfassten Objekts ist, und das Objekt vor Abschluss der Closure nil werden kann. |
unowned |
Nicht-optionale Referenz. Kann nicht nil werden. |
Wenn der Lebenszyklus der Closure kürzer oder gleich dem des erfassten Objekts ist, und garantiert wird, dass das Objekt bis zum Abschluss der Closure existiert. Wenn das Objekt vor Ausführung der Closure freigegeben wird, führt dies zu einem Absturz (unowned reference to deallocated instance). |
Beispiel für die Verwendung von weak in der Capture-Liste:
class MyClass {
var name: String = "Test"
func performAsyncOperation() {
// Die Closure erfasst `self`.
// MyClass kann vor Abschluss der asynchronen Operation freigegeben werden.
DispatchQueue.global().async { [weak self] in
guard let self = self else {
// `self` wurde freigegeben, beenden
return
}
print("Operation abgeschlossen für \(self.name)")
}
}
deinit {
print("MyClass deallokiert")
}
}
Beispiel für die Verwendung von unowned in der Capture-Liste:
class ViewController {
var dismissButton: UIButton!
func setupButton() {
dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
// Die Closure erfasst `self`.
// Wir nehmen an, dass `self` (ViewController) länger lebt oder gleich lange wie die Aktion des Buttons.
// In diesem Fall ist der Target-Action kein Closure, aber es illustriert das Konzept von `unowned` bei starken Referenzzyklen.
// Wenn es *eine* Closure wäre, z.B. im UIControl.Event-Handler,
// und ViewController während des Klicks existiert:
// dismissButton.addAction(UIAction { [unowned self] _ in
// self.dismiss(animated: true, completion: nil)
// }, for: .touchUpInside)
}
@objc func dismiss() {
// ViewController schließen
}
}
Wenn die Closure keinen Retain Cycle bildet, ist die Verwendung von weak oder unowned nicht notwendig und kann sogar zu unnötigen Prüfungen (weak) oder potenziellen Abstürzen (unowned) führen.
Beispielsweise eine Closure, die self oder ein anderes Objekt nicht erfasst, das eine starke Referenz auf die Closure hat:
func fetchData(completion: @escaping (Data) -> Void) {
URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
guard let data = data else { return }
// Hier gibt es keine Erfassung von `self` oder anderen Objekten, die einen Retain Cycle verursachen könnten.
completion(data)
}.resume()
}
In diesem Fall sind [weak self] oder [unowned self] in der Capture-Liste nicht erforderlich.