ARC: wat is het, werkingsprincipe van Automatic Reference Counting in iOS

Auteur: IT Sectr Gepubliceerd: 2026-03-29 Leestijd: 8 min

Automatic Reference Counting (ARC) — een geheugenbeheersysteem in Swift en Objective-C dat automatisch het aantal referenties naar elk object telt en het vrijgeeft wanneer de teller nul bereikt. Volgens Apple Swift Documentation, 2026 is ARC ingebouwd in de compiler en werkt het in de compilatiefase, waarbij retain/release-aanroepen op de juiste plaatsen worden ingevoegd. In tegenstelling tot Garbage Collection heeft ARC geen aparte collector-thread nodig en veroorzaakt het geen pauzes tijdens de uitvoering van de applicatie.

Belangrijkste punten

  • ARC — Automatic Reference Counting, een compilatorisch geheugenbeheersysteem in Swift en Objective-C
  • Werkingsprincipe — elk object heeft een referentieteller (retain count), bij nul wordt het object onmiddellijk vrijgegeven
  • Kwalificatoren — strong, weak en unowned bepalen hoe de referentie de teller en levenscyclus van het object beïnvloedt
  • Verschil met GC — ARC werkt deterministisch in de compilatiefase, zonder Stop-The-World-pauzes en achtergrond-collector-thread
  • Retain Cycle — het belangrijkste probleem van ARC: als twee objecten via strong naar elkaar verwijzen, wordt hun teller nooit nul

Wat is ARC?

ARC (Automatic Reference Counting) — is een compilatorisch geheugenbeheermechanisme, geïntroduceerd door Apple in Xcode 4.2 (2011) voor Objective-C en overgenomen door Swift. In tegenstelling tot handmatig geheugenbeheer (Manual Retain-Release, MRR) automatiseert ARC de retain-, release- en autorelease-aanroepen volledig door ze in de compilatiefase in te voegen zonder tussenkomst van de ontwikkelaar.

ARC is geen garbage collector. Het is een statische analyse met dynamische code-invoeging: de compiler analyseert de levensduur van objecten en plaatst retain/release op punten waar objecten worden gemaakt, gekopieerd of het bereik verlaten. Het resultaat — deterministische geheugenvrijgave: het object wordt precies op het moment verwijderd dat er niet meer naar wordt verwezen, zonder vertragingen en pauzes.

Volgens WWDC 2011 Session 323 verminderde de overgang van MRR naar ARC het aantal crash-bugs gerelateerd aan geheugen met 70% in Apple-applicaties. Ontwikkelaars hoefden niet langer handmatig retain/release in evenwicht te brengen, waardoor een hele klasse van lekken en double-free fouten werd geëlimineerd.

Hoe werkt Automatic Reference Counting

Elk object in het geheugen heeft een referentieteller (retain count). Bij het aanmaken van een object wordt de teller op 1 gezet. Wanneer een nieuwe strong-referentie naar het object verwijst, stijgt de teller (retain). Wanneer de strong-referentie verdwijnt, daalt de teller (release). Bij het bereiken van nul wordt het object onmiddellijk gedealloceerd.

De Swift-compiler voegt retain/release niet bij elke toewijzing in — hij gebruikt statische analyse voor optimalisatie. Als een object bijvoorbeeld gegarandeerd niet wordt gebruikt na overdracht, kan de compiler overbodige release/retain overslaan. Deze optimalisatie heet ARC Optimisation.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) geïnitialiseerd (retain count: 1)")
    }
    deinit {
        print("\(name) gedealloceerd")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q verlaat het bereik
    // retain count = 1
    // p verlaat het bereik
    // retain count = 0 → deinit
}

In dit voorbeeld is te zien hoe ARC de teller beheert: bij toewijzing q = p stijgt de teller, bij het verlaten van het bereik door q daalt deze. Wanneer de laatste strong-referentie verdwijnt, wordt de deinitialisator onmiddellijk aangeroepen. Geen garbage collector wacht — het geheugen wordt direct vrijgegeven.

ARC vs Garbage Collection: belangrijkste verschillen

ARC en Garbage Collection lossen dezelfde taak op — automatisch geheugenbeheer — maar met fundamenteel verschillende benaderingen. De keuze tussen hen bepaalt de architectuur van de taal: Swift (ARC) vs Java/Go (GC). Laten we de belangrijkste verschillen bekijken.

KenmerkARC (Swift/ObjC)GC (Java/Go)
VrijgavemomentDeterministisch: direct bij nul van de tellerNiet-deterministisch: bij de volgende verzameling
UitvoeringspauzesGeen (retain/release invoegingen in compilatiefase)Stop-The-World-pauzes aanwezig (2–200 ms)
OverheadVerhoging/verlaging van teller bij elke referentieDoorlopen van objectgraaf, markeren, vrijgeven
ProblemenRetain Cycle (handmatige oplossing)Heap-fragmentatie, lekken bij vergeten referenties
Extra threadNiet nodigGarbage collector-thread vereist

De belangrijkste afweging: ARC biedt een voorspelbare levensduur van objecten en nul pauzes, maar vereist van de ontwikkelaar begrip van retain cycle en de juiste keuze van weak/unowned. GC bevrijdt van deze zorgen, maar ten koste van niet-deterministische pauzes en een extra thread.

Strong, Weak en Unowned: referentiekwalificatoren in ARC

ARC definieert drie typen referentiekwalificatoren, die elk de teller en levenscyclus van het object anders beïnvloeden. De juiste keuze van de kwalificator is de basis van veilig werken met geheugen in Swift.

Strong

Strong — de standaardkwalificator. Elke strong-referentie verhoogt de retain count van het object met 1. Zolang er ten minste één strong-referentie bestaat, leeft het object. Alle klasse-eigenschappen en lokale variabelen in Swift zijn standaard strong. Strong-referenties creëren een eigendomsrelatie: object A is eigenaar van object B.

Weak

Weak — een referentie die de retain count niet verhoogt. Het object kan worden gedealloceerd, zelfs als een weak-referentie ernaar verwijst. Na deallocatie wordt de weak-referentie automatisch op nil gezet. Weak-referenties worden altijd gedeclareerd als var met een optioneel type (?). Ze worden gebruikt om retain cycles te doorbreken, met name in het delegate-patroon.

Unowned

Unowned — een niet-bezittende referentie die, net als weak, de retain count niet verhoogt. Echter, een unowned-referentie wordt niet op nil gezet na deallocatie — toegang tot een vrijgegeven object veroorzaakt een crash. Unowned wordt gebruikt wanneer gegarandeerd is dat het object minstens zo lang leeft als het refererende object. Een typisch scenario — closures en ouder-kind-relaties met gegarandeerde levensduur.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) gedealloceerd") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — niet eigenaar
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Kaart \(number) gedealloceerd") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer en CreditCard beide vrijgegeven — geen retain cycle

Hier gebruikt CreditCard een unowned-referentie naar Customer. Customer is eigenaar van de kaart (strong), en de kaart is geen eigenaar van de klant (unowned). Wanneer Customer nil wordt, worden beide objecten vrijgegeven — er ontstaat geen retain cycle. Als card.customer strong was geweest, zou de cyclus de vrijgave hebben geblokkeerd.

Typische ARC-problemen en hun oplossingen

Ondanks de automatisering is ARC geen wondermiddel. Ontwikkelaars komen verschillende typische problemen tegen die inzicht vereisen in het interne geheugenbeheermechanisme.

Retain Cycle in closures

Closures in Swift vangen externe variabelen via strong-referentie. Als een closure aan een klasse-eigenschap wordt toegewezen en self vastlegt, ontstaat een retain cycle: de klasse houdt de closure vast, de closure houdt self vast. Oplossing — capture list met weak of unowned.

swift
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] creëert een weak-referentie naar self binnen de closure. Dit doorbreekt de potentiële retain cycle. Guard let self garandeert dat het object leeft voordat de code wordt uitgevoerd. weak self — standaardpraktijk voor asynchrone closures in Swift.

Prestaties van retain/release

Hoewel retain/release lichte operaties zijn, veroorzaken frequente verhogingen/verlagingen van de teller in hete lussen overhead. In Swift 5.9+ gebruikt de compiler optimalisatie, waarbij overbodige retain/release worden verwijderd als de analysator veiligheid kan aantonen. In Objective-C kunnen retain/release echter nog steeds een knelpunt zijn in hoogbelaste scenario's met miljoenen aanroepen per seconde.

Autorelease Pool

Autorelease Pool — een mechanisme voor uitgestelde release, gebruikt in Objective-C en sommige Swift-scenario's. Objecten worden in een pool geplaatst en krijgen release bij het drainen van de pool. In lussen met veel tijdelijke objecten (bijv. JSON-parsing) vermindert het creëen van een eigen autoreleasepool het piekverbruik van werkgeheugen.

Veelgestelde vragen

Hoe verschilt ARC van handmatig geheugenbeheer (MRR)?

Bij handmatig beheer (MRR) riep de ontwikkelaar expliciet retain, release en autorelease aan. ARC voegt deze aanroepen automatisch in tijdens de compilatiefase, waardoor het risico op double-free, lekken door vergeten release en fouten in retain/release-balancering wordt geëlimineerd.

Kan ARC werken met C/C++ code?

ARC beheert alleen Objective-C-objecten en Swift-klassen. Voor C/C++-structuren en -pointers wordt ARC niet toegepast — deze objecten worden handmatig of via C++ slimme pointers (shared_ptr, unique_ptr) beheerd. Core Foundation-objecten (CFString, CGColor) vallen ook niet onder ARC.

Wanneer weak en wanneer unowned gebruiken?

weak — wanneer het object eerder kan worden gedealloceerd dan het refererende object (delegates, asynchrone closures). unowned — wanneer gegarandeerd is dat het object minstens zo lang leeft als het refererende object (ouder-kind, waarbij het kind niet kan bestaan zonder de ouder). Als u niet zeker bent, kies dan weak.

Wat zijn existentiële types en hoe beïnvloeden ze ARC?

Existentiële types (protocol as type) in Swift verpakken de waarde in een speciale container (existential container). Dit verhoogt het aantal retain/release op protocolgrenzen. In Swift 5.7+ verminderen opaque result types en some-parameters de overhead door de container te elimineren.

Hoe controleer ik de retain count in Swift?

Er is geen directe API voor het lezen van retain count in Swift — dit wordt beschouwd als een implementatiedetail. Gebruik voor diagnostiek Instruments (Allocations, Leaks) of de Memory Debugger in Xcode. Deze tools tonen het aantal levende instanties van een klasse en de retentieketens.

Samenvatting

  • ARC — een compilatorisch geheugenbeheersysteem voor Swift en Objective-C dat werkt via referentietelling
  • Principe — elk object heeft een retain count; bij nul wordt het object onmiddellijk en deterministisch vrijgegeven
  • Verschil met GC — ARC werkt zonder achtergrondthread en Stop-The-World-pauzes, maar vereist controle van retain cycle
  • Strong — verhoogt de teller; weak en unowned verhogen niet, maar unowned wordt niet gereset bij deallocatie
  • Closures — de belangrijkste oorzaak van retain cycle in Swift; capture list [weak self] — de standaardoplossing
  • Autorelease Pool — mechanisme voor uitgestelde vrijgave van tijdelijke objecten in lussen en aangepaste scenario's
  • Diagnostiek — Xcode Memory Debugger, Instruments en LeakCanary (via ObjC bridge) voor het vinden van problemen

We 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.

Bespreek het project

Lees ook