Weak Reference (svag referens) — är en referens till ett objekt som inte ökar dess referensräknare i ARC. Enligt Apple Swift Language Guide, 2026 deklareras weak-referenser med nyckelordet weak och har alltid en optionell typ. När objektet frigörs sätts alla weak-referenser till det automatiskt till nil, vilket förhindrar dinglande pekare och gör svaga referenser till en säker mekanism för att bryta retain cycles.
Huvudpunkter
weak före var; typ alltid optionell (?)Weak Reference — är en icke-ägande referens till ett objekt i ARC (Automatic Reference Counting). Till skillnad från en strong-referens som ökar objektets retain count och garanterar dess livslängd, tillåter en weak-referens att objektet frigörs även om det fortfarande finns referenser till det. Efter frigöring sätts weak-referensen automatiskt till nil — detta kallas zeroing weak.
Zeroing weak — en nyckelfunktion i Swift och Objective-C runtime. När objektets referensräknare når noll och objektet deallokeras, går runtime igenom alla weak-referenser till detta objekt (lagrade i en speciell weak-tabell) och sätter dem till nil. Detta garanterar att åtkomst till frigjort minne (use-after-free) är omöjligt via weak-referenser — varje läsning returnerar nil.
Enligt Apple WWDC 2012 Session 406 eliminerade zeroing weak-referenser en hel klass av krascher relaterade till dinglande pekare (dangling pointers), som var vanliga i manuell minneshantering (MRR). I MRR fanns svaga referenser bara som __unsafe_unretained — de nollställdes inte och åtkomst till ett frigjort objekt ledde till EXC_BAD_ACCESS.
Låt oss titta på syntaxen för att deklarera weak-referenser i båda språken i Apples ekosystem. Trots gemensam runtime skiljer sig syntaxen, men semantiken är identisk.
I Swift deklareras weak-referenser med nyckelordet weak före var. Typen måste alltid vara optionell (Type?), eftersom referensen kan nollställas när som helst. Konstanter (let) kan inte vara weak — endast variabler.
class ViewController: UIViewController {
// weak-egenskaper: endast var, endast optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ closures lagrar inte weak
// ⬆️ Fel: weak kan endast tillämpas på class-types, inte på closure
}
Viktigt: weak är endast tillämpligt på klassinstanser (class-types), AnyObject och protokoll som ärver från AnyObject. Struct, enum och closure kan inte vara weak — de är värdetyper och deltar inte i ARC.
I Objective-C deklareras weak-egenskaper via attributet __weak eller modifieraren weak i property:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// Lokal weak-variabel
__weak MyObject *weakRef = someStrongObject;
Objective-C runtime tillhandahåller också zeroing weak, men blockerar dessutom användningen av weak med C-strukturer och vissa Core Foundation-objekt. För dem används __unsafe_unretained — utan zeroing.
Svaga referenser — inte en universallösning, utan ett verktyg för specifika scenarier. Att använda weak överallt leder till överdriven komplexitet och försämrar läsbarheten. Låt oss titta på rätt användningsscenarier.
Delegat — det främsta scenariot för weak. Ägarobjektet (t.ex. UITableView) håller en strong-referens till sig själv, och delegaten (UIViewController) bör inte äga tabellen. Apple SDK garanterar att alla delegate och dataSource är weak. För egna protokoll, använd alltid weak var delegate.
När barnobjektet måste referera till föräldern (t.ex. ChildViewController för åtkomst till koordinatorn), använd en weak-referens. Föräldern äger barnet (strong), barnet observerar föräldern (weak) — retain cycle utesluten.
Capture list [weak self] — standardsättet att undvika retain cycles i closures som lagras som klassegenskaper. Om self kan frigöras innan closure är klar — weak self är obligatoriskt.
| Scenario | Weak | Strong |
|---|---|---|
| Delegate | ✅ Alltid weak | ❌ Retain cycle |
| Parent → Child | ❌ Behövs inte (förälder bör äga) | ✅ Strong |
| Child → Parent | ✅ Weak | ❌ Retain cycle |
| Asynkron callback | ✅ [weak self] | ❌ Risk för retain cycle |
| Stark koppling (owned) | ❌ unowned | ✅ Strong |
Generell regel: om objekt A äger B (A → B strong), då bör B → A vara weak eller unowned. Riktningen för strong-referenser bör alltid vara från ägaren till den underordnade.
Både weak och unowned ökar inte retain count, men skiljer sig i beteende efter objektets deallokering. Valet mellan dem är en fråga om livslängdsgarantier.
Weak: nollställs automatiskt (nil), typ alltid optionell, kräver unwrap före användning. Säker — åtkomst till nil orsakar inte krasch.
Unowned: nollställs inte, typ non-optional. Om objektet har frigjorts blir unowned-referensen en dinglande pekare — åtkomst till den orsakar en runtime-krasch. Unowned förutsätter att objektet lever minst lika länge som den refererande parten.
Weak välj om: objektet kan deallokeras när som helst (delegat efter att skärmen stängts), du inte kontrollerar objektets livslängd, eller tvivlar på garantierna. Weak — universellt säkert val.
Unowned välj om: objektet garanterat inte kan frigöras före den refererande (t.ex. Customer → CreditCard, där kortet inte finns utan kund). Unowned ger non-optional API utan unwrap, vilket är bekvämare i koden.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// Stark koppling: Order äger 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 lever inte utan Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// Exempel med weak: delegat utan livslängdsgaranti
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — delegat kan gå iväg
}
I exemplet använder Item unowned, eftersom ett orderelement inte kan existera utan själva ordern — livslängdsgarantin är järnhård. NetworkService använder weak, eftersom delegaten (t.ex. ViewController) kan stängas och frigöras när som helst.
Svaga referenser — ett kraftfullt verktyg, men de har begränsningar som är viktiga att förstå för korrekt användning inom iOS-utveckling.
Svaga referenser är långsammare än strong: vid varje åtkomst kontrollerar runtime om objektet har frigjorts (lookup i weak-tabellen). I de allra flesta scenarier är skillnaden omärkbar, men i heta loopar med miljontals åtkomster kan weak vara en flaskhals. För scenarier med hög belastning, använd strong och omorganisera arkitekturen.
Struct, enum, tuple — värdetyper som inte deltar i ARC. Försök att deklarera weak struct leder till kompileringsfel. För att lagra en svag referens till en värdetyp, använd en wrapper i class-type eller en closure.
Zeroing weak är trådsäker: om objektet frigörs på en tråd nollställs weak-referensen på alla trådar atomärt. Dock kan intervallet mellan läsning av en weak-referens och användning leda till en race condition — objektet frigörs mellan att man får weak-referensen och använder den. Lösning: strong-infångning av den svaga referensen i en lokal variabel.
// Race condition med weak i flertrådad miljö
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf kan vara nil mellan kontroll och användning
if weakSelf != nil {
weakSelf!.doSomething() // KRASCH om den blev nil
}
}
}
// ✅ Fix: stark infångning för användningens varaktighet
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — lokal strong-referens
}
}
I den säkra varianten fångas weak self, packas sedan omedelbart upp i en lokal strong-variabel strongSelf. Om self fortfarande lever — förblir den vid liv under blockets exekvering. Om inte — guard aktiveras och koden körs inte. Detta idiom — standardmönstret för asynkrona closures i Swift.
IBOutlet i Interface Builder bör vara weak, eftersom vyns hierarki redan har en strong-referens till subview. Att duplicera strong-referensen i kontrollern skapar inte en retain cycle, men är överflödigt. Svag referens till outlet — Apples rekommendation, även om många utvecklare använder strong för att förenkla koden.
Vanliga frågor
Nej, weak kan bara peka på ett befintligt objekt eller nil. När du skapar ett nytt objekt får du först en strong-referens (via initieraren), och först därefter kan du tilldela en svag referens. weak nil i början — ett normalt tillstånd.
Weak är baserat på ARC som bara hanterar referenstyper (klasser). Värdetyper (struct, enum) kopieras vid tilldelning och har inget retain count. För svag koppling av värdetyper, använd closures eller en wrapper i en class med en weak-egenskap.
Varje åtkomst till en weak-referens utför en lookup i runtime-tabellen. I en loop med miljontals iterationer kan detta vara 2–5 gånger långsammare än en strong-referens. För heta vägar, kopiera weak till en lokal strong-variabel före loopen.
När alla strong-referenser till objektet förloras — i slutet av scopet, vid återställning av en egenskap, vid stängning av en skärm. I en flertrådad miljö kan detta hända mellan två kodrader. Kontrollera alltid weak via guard let eller if let.
Semantiskt identiskt: båda tillhandahåller zeroing weak. Skillnader: Swift kräver optionell typ och var, Objective-C använder property-modifieraren. Objective-C stöder också __unsafe_unretained — en svag referens utan zeroing (risk för dinglande pekare).
Sammanfattning
weak var + optionell typ; endast class-types och AnyObject-protokollVi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också