Weak Reference (zwakke referentie) — is een verwijzing naar een object die de retentieteller in ARC niet verhoogt. Volgens Apple Swift Language Guide, 2026 worden weak-referenties gedeclareerd met het sleutelwoord weak en hebben ze altijd een optioneel type. Wanneer het object wordt vrijgegeven, worden alle weak-referenties ernaar automatisch op nil gezet, wat dangling pointers voorkomt en zwakke referenties een veilig mechanisme maakt voor het verbreken van retain cycles.
Belangrijkste
weak voor var; type altijd optioneel (?)Weak Reference — is een niet-bezittende verwijzing naar een object in ARC (Automatic Reference Counting). In tegenstelling tot een strong-referentie, die de retain count van een object verhoogt en het leven garandeert, staat een weak-referentie toe dat het object wordt vrijgegeven, zelfs als er nog naar wordt verwezen. Na vrijgave wordt de weak-referentie automatisch op nil gezet — dit heet zeroing weak.
Zeroing weak — een belangrijk kenmerk van de Swift en Objective-C runtime. Wanneer de referentieteller van een object nul bereikt en het object wordt gedealloceerd, doorloopt de runtime alle weak-referenties naar dit object (opgeslagen in een speciale weak-tabel) en zet ze op nil. Dit garandeert dat toegang tot vrijgegeven geheugen (use-after-free) onmogelijk is via weak-referenties — elke lezing retourneert nil.
Volgens Apple WWDC 2012 Session 406 hebben zeroing weak-referenties een hele klasse crash-bugs gerelateerd aan dangling pointers geëlimineerd, die wijdverbreid waren in handmatig geheugenbeheer (MRR). In MRR bestonden zwakke referenties alleen als __unsafe_unretained — ze werden niet op nul gezet en toegang tot een vrijgegeven object leidde tot EXC_BAD_ACCESS.
Laten we de syntaxis van het declareren van weak-referenties in beide talen van het Apple-ecosysteem bekijken. Ondanks de gedeelde runtime verschilt de syntaxis, maar de semantiek is identiek.
In Swift worden weak-referenties gedeclareerd met het sleutelwoord weak voor var. Het type moet altijd optioneel zijn (Type?), omdat de referentie op elk moment op nul kan worden gezet. Constanten (let) kunnen niet weak zijn — alleen variabelen.
class ViewController: UIViewController {
// weak-eigenschappen: alleen var, alleen optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ closures slaan weak niet op
// ⬆️ Fout: weak kan alleen worden toegepast op class-types, niet op closure
}
Belangrijk: weak is alleen van toepassing op klasse-instanties (class-types), AnyObject en protocollen die overerven van AnyObject. Struct, enum en closure kunnen niet weak zijn — het zijn waardetypen en nemen niet deel aan ARC.
In Objective-C worden weak-eigenschappen gedeclareerd via het attribuut __weak of de modifier weak in de property:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// Lokale weak-variabele
__weak MyObject *weakRef = someStrongObject;
De Objective-C runtime biedt ook zeroing weak, maar blokkeert daarnaast het gebruik van weak met C-structuren en sommige Core Foundation-objecten. Daarvoor wordt __unsafe_unretained gebruikt — zonder zeroing.
Zwakke referenties — geen universele oplossing, maar een hulpmiddel voor specifieke scenario's. Overal weak gebruiken leidt tot overmatige complexiteit en vermindert de leesbaarheid. Laten we de juiste toepassingsscenario's bekijken.
Delegate — het belangrijkste scenario voor weak. Het eigenaar-object (bijv. UITableView) houdt een strong-referentie naar zichzelf vast, en de delegat (UIViewController) mag geen eigenaar van de tabel zijn. Apple SDK garandeert dat alle delegate en dataSource weak zijn. Gebruik voor eigen protocollen altijd weak var delegate.
Wanneer een kind-object naar de ouder moet verwijzen (bijv. ChildViewController voor toegang tot de coördinator), gebruik dan een weak-referentie. De ouder is eigenaar van het kind (strong), het kind observeert de ouder (weak) — retain cycle uitgesloten.
Capture list [weak self] — de standaardmanier om retain cycles te vermijden in closures die als eigenschappen van een klasse worden opgeslagen. Als self kan worden vrijgegeven voordat de closure is voltooid — is weak self verplicht.
| Scenario | Weak | Strong |
|---|---|---|
| Delegate | ✅ Altijd weak | ❌ Retain cycle |
| Parent → Child | ❌ Niet nodig (ouder moet eigenaar zijn) | ✅ Strong |
| Child → Parent | ✅ Weak | ❌ Retain cycle |
| Asynchrone callback | ✅ [weak self] | ❌ Risico op retain cycle |
| Sterke binding (owned) | ❌ unowned | ✅ Strong |
Algemene regel: als object A eigenaar is van B (A → B strong), dan moet B → A weak of unowned zijn. De richting van strong-referenties moet altijd van eigenaar naar ondergeschikte zijn.
Zowel weak als unowned verhogen de retain count niet, maar verschillen in gedrag na deallocatie van het object. De keuze ertussen is een kwestie van levensduurgaranties.
Weak: wordt automatisch op nul gezet (nil), type altijd optional, vereist unwrap voor gebruik. Veilig — toegang tot nil veroorzaakt geen crash.
Unowned: wordt niet op nul gezet, type non-optional. Als het object is vrijgegeven, wordt de unowned-referentie een dangling pointer — toegang ertoe veroorzaakt een runtime crash. Unowned veronderstelt dat het object niet korter leeft dan de verwijzende partij.
Weak kies als: het object op elk moment gedealloceerd kan worden (delegat na het sluiten van een scherm), je de levensduur van het object niet controleert of twijfelt aan de garanties. Weak — universele veilige keuze.
Unowned kies als: het object gegarandeerd niet eerder kan worden vrijgegeven dan de verwijzende partij (bijv. Customer → CreditCard, waar de kaart niet bestaat zonder klant). Unowned geeft een non-optional API zonder unwrap, wat handiger is in code.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// Sterke binding: Order is eigenaar van 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 leeft niet zonder Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// Voorbeeld met weak: delegat zonder levensduurgarantie
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — delegat kan weggaan
}
In het voorbeeld gebruikt Item unowned, omdat een bestellingselement niet kan bestaan zonder de bestelling zelf — de levensduurgarantie is ijzersterk. NetworkService gebruikt weak, omdat de delegat (bijv. ViewController) op elk moment gesloten en vrijgegeven kan worden.
Zwakke referenties — een krachtig hulpmiddel, maar ze hebben beperkingen die belangrijk zijn om te begrijpen voor correct gebruik in iOS-ontwikkeling.
Zwakke referenties zijn langzamer dan strong: bij elke toegang controleert de runtime of het object is vrijgegeven (lookup in de weak-tabel). In de overgrote meerderheid van scenario's is het verschil niet merkbaar, maar in hete lussen met miljoenen toegangen kan weak een knelpunt zijn. Gebruik voor scenario's met hoge belasting strong en reorganiseer de architectuur.
Struct, enum, tuple — waardetypen die niet deelnemen aan ARC. Een poging om weak struct te declareren leidt tot een compilatiefout. Gebruik voor het opslaan van een zwakke referentie naar een waardetype een wrapper in een class-type of een closure.
Zeroing weak is thread-safe: als een object op één thread wordt vrijgegeven, wordt de weak-referentie op alle threads atomair op nul gezet. De tussenruimte tussen het lezen van een weak-referentie en het gebruik ervan kan echter leiden tot een raceconditie — het object wordt vrijgegeven tussen het verkrijgen van de weak-referentie en het gebruik ervan. Oplossing: strong-vastleggen van de zwakke referentie in een lokale variabele.
// Raceconditie met weak in multithreading
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf kan nil zijn tussen controle en gebruik
if weakSelf != nil {
weakSelf!.doSomething() // CRASH als het nil werd
}
}
}
// ✅ Oplossing: strong-vastleggen voor de duur van gebruik
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — lokale strong-referentie
}
}
In de veilige variant wordt weak self vastgelegd en vervolgens onmiddellijk uitgepakt in een lokale strong-variabele strongSelf. Als self nog leeft — blijft het leven tijdens de uitvoering van het blok. Zo niet — dan wordt guard geactiveerd en wordt de code niet uitgevoerd. Dit idiom — het standaard patroon voor asynchrone closures in Swift.
IBOutlet in Interface Builder moeten weak zijn, omdat de view-hiërarchie al een strong-referentie naar de subview heeft. Het dupliceren van een strong-referentie in de controller creëert geen retain cycle, maar is overbodig. Een zwakke referentie naar een outlet — aanbeveling van Apple, hoewel veel ontwikkelaars strong gebruiken voor eenvoudigere code.
Veelgestelde vragen
Nee, weak kan alleen naar een bestaand object of nil verwijzen. Bij het maken van een nieuw object krijg je eerst een strong-referentie (via de initialisator), en pas daarna kun je een zwakke referentie toewijzen. weak nil aan het begin — een normale toestand.
Weak is gebaseerd op ARC, dat alleen referentietypen (klassen) beheert. Waardetypen (struct, enum) worden gekopieerd bij toewijzing en hebben geen retain count. Gebruik voor zwakke koppeling van waardetypen closures of wrappers in een class met een weak-eigenschap.
Elke toegang tot een weak-referentie voert een lookup uit in de runtime-tabel. In een lus met miljoenen iteraties kan dit 2–5 keer langzamer zijn dan een strong-referentie. Kopieer voor hete paden weak naar een lokale strong-variabele vóór de lus.
Wanneer alle strong-referenties naar het object verloren gaan — aan het einde van de scope, bij het opnieuw instellen van een eigenschap, bij het sluiten van een scherm. In een multithreading-omgeving kan dit tussen twee regels code gebeuren. Controleer weak altijd via guard let of if let.
Semantisch identiek: beide bieden zeroing weak. Verschillen: Swift vereist een optional-type en var, Objective-C gebruikt de property-modifier. Objective-C ondersteunt ook __unsafe_unretained — een zwakke referentie zonder zeroing (risico op dangling pointer).
Samenvatting
weak var + optional type; alleen class-types en AnyObject-protocollenWe ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook