Weak Reference (schwache Referenz) ist ein Verweis auf ein Objekt, der dessen Retain Count in ARC nicht erhöht. Laut Apple Swift Language Guide, 2026 werden schwache Referenzen mit dem Schlüsselwort weak deklariert und sind immer optional. Wenn das Objekt freigegeben wird, werden alle schwachen Referenzen darauf automatisch auf nil gesetzt, was hängende Zeiger verhindert und schwache Referenzen zu einem sicheren Mechanismus zum Auflösen von Retain Cycles macht.
Wichtigste Punkte
weak vor var; Typ ist immer optional (?)Weak Reference ist ein nicht-besitzender Verweis auf ein Objekt in ARC (Automatic Reference Counting). Im Gegensatz zu einer starken Referenz, die den Retain Count des Objekts erhöht und seine Lebensdauer garantiert, erlaubt eine schwache Referenz, dass das Objekt freigegeben wird, selbst wenn noch darauf verwiesen wird. Nach der Freigabe wird die schwache Referenz automatisch auf nil gesetzt — dies wird als zeroing weak bezeichnet.
Zeroing weak ist eine Schlüsselfunktion der Swift- und Objective-C-Laufzeitumgebung. Wenn der Referenzzähler eines Objekts Null erreicht und das Objekt freigegeben wird, durchläuft die Laufzeitumgebung alle schwachen Referenzen auf dieses Objekt (gespeichert in einer speziellen Weak-Tabelle) und setzt sie auf nil. Dies stellt sicher, dass der Zugriff auf freigegebenen Speicher (Use-after-free) über schwache Referenzen unmöglich ist — jeder Lesezugriff gibt nil zurück.
Laut Apple WWDC 2012 Session 406 haben Zeroing-Weak-Referenzen eine ganze Klasse von Absturzfehlern im Zusammenhang mit hängenden Zeigern (Dangling Pointers) beseitigt, die in der manuellen Speicherverwaltung (MRR) üblich waren. In MRR existierten schwache Referenzen nur als __unsafe_unretained — sie wurden nicht genullt, und der Zugriff auf ein freigegebenes Objekt führte zu EXC_BAD_ACCESS.
Betrachten wir die Syntax zum Deklarieren schwacher Referenzen in beiden Sprachen des Apple-Ökosystems. Trotz der gemeinsamen Laufzeitumgebung unterscheidet sich die Syntax, aber die Semantik ist identisch.
In Swift werden schwache Referenzen mit dem Schlüsselwort weak vor var deklariert. Der Typ muss immer optional sein (Typ?), da die Referenz jederzeit nil werden kann. Konstanten (let) können nicht weak sein — nur Variablen.
class ViewController: UIViewController {
// weak properties: only var, only optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ Closures speichern weak nicht
// ⬆️ Fehler: weak kann nur auf Klassentypen angewendet werden, nicht auf Closures
}
Wichtig: weak ist nur auf Klasseninstanzen (Klassentypen), AnyObject und von AnyObject abgeleitete Protokolle anwendbar. Struct, Enum und Closures können nicht weak sein — sie sind Wertetypen und nehmen nicht an ARC teil.
In Objective-C werden schwache Eigenschaften mit dem Attribut __weak oder dem Modifikator weak in Eigenschaftsdeklarationen deklariert:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// Lokale weak-Variable
__weak MyObject *weakRef = someStrongObject;
Die Objective-C-Laufzeitumgebung bietet ebenfalls zeroing weak, blockiert jedoch zusätzlich die Verwendung von weak mit C-Strukturen und einigen Core Foundation-Objekten. Für diese wird __unsafe_unretained verwendet — ohne Zeroing.
Schwache Referenzen sind keine universelle Lösung, sondern ein Werkzeug für bestimmte Szenarien. Die überall Verwendung von weak führt zu unnötiger Komplexität und beeinträchtigt die Lesbarkeit. Betrachten wir die richtigen Anwendungsszenarien.
Delegaten — das primäre Szenario für weak. Das besitzende Objekt (z. B. UITableView) hält eine starke Referenz auf sich selbst, während der Delegat (UIViewController) die Tabelle nicht besitzen sollte. Das Apple SDK garantiert, dass alle Delegaten und dataSources weak sind. Verwenden Sie für Ihre eigenen Protokolle immer weak var delegate.
Wenn ein Kindobjekt auf sein Elternobjekt verweisen muss (z. B. ChildViewController, der auf einen Koordinator zugreift), verwenden Sie eine schwache Referenz. Das Elternteil besitzt das Kind (strong), das Kind beobachtet das Elternteil (weak) — der Retain Cycle ist eliminiert.
Capture List [weak self] — die Standardmethode, um Retain Cycles in Closures zu vermeiden, die als Klasseneigenschaften gespeichert werden. Wenn self vor Abschluss der Closure freigegeben werden kann, ist weak self obligatorisch.
| Szenario | Weak | Strong |
|---|---|---|
| Delegate | ✅ Immer weak | ❌ Retain Cycle |
| Eltern → Kind | ❌ Nicht nötig (Eltern sollten besitzen) | ✅ Strong |
| Kind → Eltern | ✅ Weak | ❌ Retain Cycle |
| Asynchroner Callback | ✅ [weak self] | ❌ Retain-Cycle-Risiko |
| Starke Kopplung (owned) | ❌ unowned | ✅ Strong |
Allgemeine Regel: Wenn Objekt A B besitzt (A → B strong), dann sollte B → A weak oder unowned sein. Die Richtung der starken Referenzen sollte immer vom Besitzer zum Untergebenen sein.
Sowohl weak als auch unowned erhöhen den Retain Count nicht, unterscheiden sich jedoch im Verhalten nach der Freigabe des Objekts. Die Wahl zwischen ihnen ist eine Frage der Lebensdauergarantien.
Weak: wird automatisch nil, Typ ist immer optional, erfordert Entpacken vor der Verwendung. Sicher — Zugriff auf nil verursacht keinen Absturz.
Unowned: wird nicht nil, Typ ist nicht optional. Wenn das Objekt freigegeben wird, wird eine unowned-Referenz zu einem hängenden Zeiger — der Zugriff darauf verursacht einen Laufzeitabsturz. Unowned geht davon aus, dass das Objekt mindestens so lange lebt wie die referenzierende Seite.
Wählen Sie weak, wenn: das Objekt jederzeit freigegeben werden kann (Delegat nach Schließen des Bildschirms), Sie die Lebensdauer des Objekts nicht kontrollieren oder Sie sich bei den Garantien unsicher sind. Weak ist die universelle sichere Wahl.
Wählen Sie unowned, wenn: das Objekt garantiert nicht vor dem referenzierenden Objekt freigegeben wird (z. B. Kunde → Kreditkarte, wo die Karte ohne den Kunden nicht existiert). Unowned bietet eine nicht-optionale API ohne Entpacken, was im Code bequemer ist.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// Starke Beziehung: Order besitzt Item
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — Item lebt nicht ohne Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// Beispiel mit weak: Delegat ohne Lebensdauergarantie
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — Delegat kann verschwinden
}
Im Beispiel verwendet Item unowned, weil ein Bestellposten ohne die Bestellung selbst nicht existieren kann — die Lebensdauergarantie ist eisern. NetworkService verwendet weak, weil der Delegat (z. B. ViewController) jederzeit geschlossen und freigegeben werden kann.
Schwache Referenzen sind ein leistungsstarkes Werkzeug, haben aber Einschränkungen, die für die korrekte Verwendung in der iOS-Entwicklung wichtig zu verstehen sind.
Schwache Referenzen sind langsamer als starke: Bei jedem Zugriff prüft die Laufzeitumgebung, ob das Objekt freigegeben wurde (Lookup in der Weak-Tabelle). In den allermeisten Szenarien ist der Unterschied nicht spürbar, aber in heißen Schleifen mit Millionen von Zugriffen kann weak zum Engpass werden. Verwenden Sie für Hochlastszenarien strong und reorganisieren Sie die Architektur.
Struct, Enum, Tuple — Wertetypen, die nicht an ARC teilnehmen. Der Versuch, ein weak struct zu deklarieren, führt zu einem Kompilierungsfehler. Um eine schwache Referenz auf einen Wertetyp zu speichern, verwenden Sie einen Wrapper in einem Klassentyp oder eine Closure.
Zeroing weak ist threadsicher: Wenn ein Objekt in einem Thread freigegeben wird, wird die schwache Referenz in allen Threads atomar genullt. Das Fenster zwischen dem Lesen einer schwachen Referenz und dem Dereferenzieren kann jedoch zu einer Wettlaufsituation führen — das Objekt wird zwischen dem Erhalten der schwachen Referenz und ihrer Verwendung freigegeben. Lösung: Strong Capture der schwachen Referenz in eine lokale Variable.
// Race Condition mit weak in Multithreading
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf kann zwischen Prüfung und Verwendung nil sein
if weakSelf != nil {
weakSelf!.doSomething() // CRASH wenn nil wird
}
}
}
// ✅ Korrektur: starker Capture während der Verwendung
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — lokale starke Referenz
}
}
In der sicheren Version wird weak self erfasst und dann sofort in eine lokale starke Variable strongSelf entpackt. Wenn self noch lebt, bleibt es für die Dauer des Blocks am Leben. Wenn nicht, wird guard ausgelöst und der Code wird nicht ausgeführt. Dieses Idiom ist das Standardmuster für asynchrone Closures in Swift.
IBOutlet in Interface Builder sollten weak sein, da die Ansichtshierarchie bereits eine starke Referenz auf die Unteransicht hält. Das Duplizieren einer starken Referenz im Controller erzeugt keinen Retain Cycle, ist aber redundant. Eine schwache Referenz auf ein Outlet ist Apples Empfehlung, obwohl viele Entwickler aus Gründen der Code-Einfachheit strong verwenden.
Häufig gestellte Fragen
Nein, weak kann nur auf ein vorhandenes Objekt oder nil zeigen. Beim Erstellen eines neuen Objekts erhalten Sie zunächst eine starke Referenz (über einen Initialisierer), und erst dann können Sie eine schwache Referenz zuweisen. Ein weak nil am Anfang ist ein normaler Zustand.
Weak basiert auf ARC, das nur Referenztypen (Klassen) verwaltet. Wertetypen (struct, enum) werden bei der Zuweisung kopiert und haben keinen Retain Count. Für schwache Beziehungen mit Wertetypen verwenden Sie Closures oder Wrapper in einer Klasse mit einer weak-Eigenschaft.
Jeder Zugriff auf eine schwache Referenz führt einen Lookup in der Laufzeittabelle durch. In einer Schleife mit Millionen von Iterationen kann dies 2–5 Mal langsamer sein als eine starke Referenz. Kopieren Sie für heiße Pfade weak vor der Schleife in eine lokale starke Variable.
Wenn alle starken Referenzen auf das Objekt verloren gehen — am Ende des Gültigkeitsbereichs, beim Neuzuweisen einer Eigenschaft oder beim Schließen eines Bildschirms. In einer Multithread-Umgebung kann dies zwischen zwei Codezeilen passieren. Überprüfen Sie schwache Referenzen immer mit guard let oder if let.
Semantisch identisch: beide bieten zeroing weak. Unterschiede: Swift erfordert einen optionalen Typ und var, Objective-C verwendet einen Eigenschaftsmodifikator. Objective-C unterstützt auch __unsafe_unretained — eine schwache Referenz ohne Zeroing (Risiko eines hängenden Zeigers).
Zusammenfassung
weak var + optionaler Typ; nur Klassentypen und AnyObject-ProtokolleWir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch