Automatic Reference Counting (ARC) — ett minneshanteringssystem i Swift och Objective-C som automatiskt räknar antalet referenser till varje objekt och frigör det när räknaren når noll. Enligt Apple Swift Documentation, 2026 är ARC inbäddat i kompilatorn och fungerar i kompileringsfasen, och infogar retain/release-anrop på rätt ställen. Till skillnad från Garbage Collection kräver ARC ingen separat insamlingstråd och skapar inga pauser under applikationens körning.
Huvudpunkter
ARC (Automatic Reference Counting) — är en mekanism för minneshantering på kompilatornivå, introducerad av Apple i Xcode 4.2 (2011) för Objective-C och ärvd av Swift. Till skillnad från manuell minneshantering (Manual Retain-Release, MRR) automatiserar ARC helt anropen retain, release och autorelease, och infogar dem i kompileringsfasen utan utvecklarens medverkan.
ARC är ingen garbage collector. Det är en statisk analys med dynamisk kodinfogning: kompilatorn analyserar objektens livslängd och placerar retain/release vid punkter där objekt skapas, kopieras eller lämnar omfattningen. Resultatet — deterministisk minnesfrigöring: objektet tas bort precis när det inte längre refereras, utan fördröjningar och pauser.
Enligt WWDC 2011 Session 323 minskade övergången från MRR till ARC antalet crash-buggar relaterade till minne med 70 % i Apples applikationer. Utvecklare slutade att manuellt balansera retain/release, vilket eliminerade en hel klass av läckor och double-free-fel.
Varje objekt i minnet har en referensräknare (retain count). När ett objekt skapas sätts räknaren till 1. När en ny strong-referens pekar på objektet ökar räknaren (retain). När strong-referensen försvinner minskar räknaren (release). Vid noll frigörs objektet omedelbart.
Swift-kompilatorn infogar inte retain/release vid varje tilldelning — den använder statisk analys för optimering. Till exempel, om ett objekt garanterat inte används efter överföring, kan kompilatorn hoppa över onödig release/retain. Denna optimering kallas ARC Optimisation.
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) initierad (retain count: 1)")
}
deinit {
print("\(name) frigjord")
}
}
func testARC() {
let p = Person(name: "Alice") // retain count = 1
let q = p // retain count = 2
// q lämnar omfattningen
// retain count = 1
// p lämnar omfattningen
// retain count = 0 → deinit
}
I detta exempel syns hur ARC hanterar räknaren: vid tilldelningen q = p ökar räknaren, när q lämnar omfattningen minskar den. När den sista strong-referensen försvinner anropas deinitialiseraren omedelbart. Ingen garbage collector väntar — minnet frigörs direkt.
ARC och Garbage Collection löser samma uppgift — automatisk minneshantering — men med fundamentalt olika angreppssätt. Valet mellan dem bestämmer språkets arkitektur: Swift (ARC) vs Java/Go (GC). Låt oss titta på huvudskillnaderna.
| Egenskap | ARC (Swift/ObjC) | GC (Java/Go) |
|---|---|---|
| Frigöringstillfälle | Deterministiskt: omedelbart vid räknarens nollställning | Icke-deterministiskt: vid nästa insamling |
| Exekveringspauser | Inga (retain/release-infogningar i kompileringsfasen) | Stop-The-World-pauser förekommer (2–200 ms) |
| Overhead | Ökning/minskning av räknare vid varje referens | Genomgång av objektgraf, markering, frigöring |
| Problem | Retain Cycle (manuell lösning) | Heap-fragmentering, läckor vid glömda referenser |
| Extra tråd | Krävs inte | Kräver garbage collector-tråd |
Den viktigaste avvägningen: ARC ger förutsägbar objektlivslängd och noll pauser, men kräver att utvecklaren förstår retain cycle och väljer rätt weak/unowned. GC befriar från dessa bekymmer, men till priset av icke-deterministiska pauser och en extra tråd.
ARC definierar tre typer av referenskvalificerare, som var och en påverkar räknaren och objektets livscykel olika. Rätt val av kvalificerare är grunden för säkert arbete med minne i Swift.
Strong — standardkvalificeraren. Varje strong-referens ökar objektets retain count med 1. Så länge det finns minst en strong-referens lever objektet. Alla klassegenskaper och lokala variabler i Swift är som standard strong. Strong-referenser skapar en äganderelation: objekt A äger objekt B.
Weak — en referens som inte ökar retain count. Objektet kan frigöras även om en weak-referens pekar på det. Efter frigöring sätts weak-referensen automatiskt till nil. Weak-referenser deklareras alltid som var med en optionell typ (?). De används för att bryta retain cycle, särskilt i delegate-mönstret.
Unowned — en icke-ägande referens som, liksom weak, inte ökar retain count. Emellertid sätts unowned-referensen inte till nil efter frigöring — åtkomst till ett frigjort objekt orsakar en krasch. Unowned används när det är garanterat att objektet lever minst lika länge som det refererande objektet. Typiskt scenario — closures och förälder-barn-relationer med garanterad livslängd.
class Customer {
let name: String
var card: CreditCard? // strong
init(name: String) { self.name = name }
deinit { print("\(name) frigjord") }
}
class CreditCard {
let number: String
unowned let customer: Customer // unowned — äger inte
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Kort \(number) frigjort") }
}
var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer och CreditCard båda frigjorda — ingen retain cycle
Här använder CreditCard en unowned-referens till Customer. Customer äger kortet (strong), och kortet äger inte kunden (unowned). När Customer blir nil frigörs båda objekten — ingen retain cycle uppstår. Om card.customer hade varit strong skulle cykeln ha blockerat frigöringen.
Trots automatiseringen är ARC inget universalmedel. Utvecklare stöter på flera typiska problem som kräver förståelse av den interna minneshanteringsmekanismen.
Closures i Swift fångar externa variabler via strong-referens. Om en closure tilldelas en klassegenskap och fångar self — uppstår en retain cycle: klassen håller closuren, closuren håller self. Lösning — capture list med weak eller unowned.
class NetworkManager {
var completionHandler: ((Data?) -> Void)?
var data: Data?
func fetchData() {
completionHandler = { [weak self] result in
guard let self else { return }
self.data = result
self.processResult()
}
}
func processResult() { }
}
Capture list [weak self] skapar en weak-referens till self inuti closuren. Detta bryter den potentiella retain cycle. Guard let self garanterar att objektet lever innan koden exekveras. weak self — standardpraxis för asynkrona closures i Swift.
Även om retain/release är lätta operationer, orsakar frekventa ökningar/minskningar av räknaren i heta loopar overhead. I Swift 5.9+ använder kompilatorn optimering, där onödiga retain/release tas bort om analysatorn kan bevisa säkerhet. Men i Objective-C kan retain/release fortfarande vara en flaskhals i högbelastningsscenarier med miljontals anrop per sekund.
Autorelease Pool — en mekanism för fördröjd release, som används i Objective-C och vissa Swift-scenarier. Objekt placeras i en pool och får release när poolen töms. I loopar med många temporära objekt (t.ex. JSON-tolkning) minskar skapandet av en egen autoreleasepool toppförbrukningen av arbetsminne.
Vanliga frågor
Vid manuell hantering (MRR) anropade utvecklaren explicit retain, release och autorelease. ARC infogar dessa anrop automatiskt i kompileringsfasen, vilket eliminerar risken för double-free, läckor på grund av glömd release och fel i retain/release-balansering.
ARC hanterar endast Objective-C-objekt och Swift-klasser. För C/C++-strukturer och pekare tillämpas inte ARC — dessa objekt hanteras manuellt eller via C++ smarta pekare (shared_ptr, unique_ptr). Core Foundation-objekt (CFString, CGColor) omfattas inte heller av ARC.
weak — när objektet kan frigöras före det refererande objektet (delegater, asynkrona closures). unowned — när det är garanterat att objektet lever minst lika länge som det refererande objektet (förälder-barn, där barnet inte kan existera utan föräldern). Om du är osäker — välj weak.
Existentiella typer (protocol as type) i Swift packar värdet i en speciell behållare (existential container). Detta ökar antalet retain/release vid protokollgränser. I Swift 5.7+ minskar opaque result types och some-parametrar overhead genom att eliminera behållaren.
Det finns inget direkt API för att läsa retain count i Swift — detta anses vara en implementeringsdetalj. För diagnostik, använd Instruments (Allocations, Leaks) eller Memory Debugger i Xcode. Dessa verktyg visar antalet levande instanser av klassen och retentionskedjor.
Sammanfattning
Vi 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å