ARC: Was es ist, Funktionsprinzip von Automatic Reference Counting in iOS

Autor: IT Sectr Veröffentlicht: 2026-03-29 Lesezeit: 8 Min.

Automatic Reference Counting (ARC) ist ein Speicherverwaltungssystem in Swift und Objective-C, das automatisch die Anzahl der Referenzen auf jedes Objekt zählt und es freigibt, wenn der Zähler null erreicht. Laut Apple Swift Documentation, 2026 ist ARC in den Compiler eingebettet und arbeitet zur Compile-Zeit, indem es retain/release-Aufrufe an den richtigen Stellen einfügt. Im Gegensatz zu Garbage Collection benötigt ARC keinen separaten Collector-Thread und erzeugt keine Pausen während der Ausführung der Anwendung.

Wichtige Punkte

  • ARC — Automatic Reference Counting, compilerbasiertes Speicherverwaltungssystem in Swift und Objective-C
  • Funktionsprinzip — jedes Objekt hat einen Referenzzähler (retain count); bei Null wird das Objekt sofort freigegeben
  • Qualifikatoren — strong, weak und unowned bestimmen, wie eine Referenz den Zähler und den Lebenszyklus des Objekts beeinflusst
  • Unterschied zu GC — ARC arbeitet deterministisch zur Compile-Zeit, ohne Stop-The-World-Pausen oder Hintergrund-Collector-Thread
  • Retain Cycle — das Hauptproblem von ARC: wenn zwei Objekte sich gegenseitig über strong referenzieren, wird ihr Zähler nie null

Was ist ARC?

ARC (Automatic Reference Counting) ist ein compilerbasiertes Speicherverwaltungssystem, das von Apple in Xcode 4.2 (2011) für Objective-C eingeführt und von Swift übernommen wurde. Im Gegensatz zur manuellen Speicherverwaltung (Manual Retain-Release, MRR) automatisiert ARC die retain-, release- und autorelease-Aufrufe vollständig und fügt sie zur Compile-Zeit ohne Eingreifen des Entwicklers ein.

ARC ist kein Garbage Collector. Es ist eine statische Analyse mit dynamischer Code-Einfügung: der Compiler analysiert die Lebensdauer von Objekten und platziert retain/release an den Stellen, an denen Objekte erstellt, kopiert oder den Gültigkeitsbereich verlassen. Das Ergebnis ist eine deterministische Speicherfreigabe: das Objekt wird genau dann gelöscht, wenn keine Referenzen mehr darauf zeigen, ohne Verzögerungen oder Pausen.

Laut WWDC 2011 Session 323 reduzierte der Wechsel von MRR zu ARC speicherbezogene Crash-Fehler in Apple-Anwendungen um 70%. Entwickler mussten retain/release nicht mehr manuell ausbalancieren, wodurch eine ganze Klasse von Speicherlecks und double-free-Fehlern beseitigt wurde.

Wie Automatic Reference Counting funktioniert

Jedes Objekt im Speicher hat einen Referenzzähler (retain count). Wenn ein Objekt erstellt wird, wird der Zähler auf 1 gesetzt. Wenn eine neue strong-Referenz auf das Objekt zeigt, erhöht sich der Zähler (retain). Wenn eine strong-Referenz verschwindet, verringert sich der Zähler (release). Bei Erreichen von Null wird das Objekt sofort freigegeben.

Der Swift-Compiler fügt retain/release nicht bei jeder Zuweisung ein — er verwendet statische Analyse zur Optimierung. Wenn beispielsweise garantiert ist, dass ein Objekt nach der Übergabe nicht mehr verwendet wird, kann der Compiler ein unnötiges release/retain überspringen. Diese Optimierung heißt ARC Optimization.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) initialized (retain count: 1)")
    }
    deinit {
        print("\(name) deallocated")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q verlässt den Gültigkeitsbereich
    // retain count = 1
    // p verlässt den Gültigkeitsbereich
    // retain count = 0 → deinit
}

Dieses Beispiel zeigt, wie ARC den Zähler verwaltet: bei der Zuweisung q = p erhöht sich der Zähler; wenn q den Gültigkeitsbereich verlässt, verringert er sich. Wenn die letzte strong-Referenz verschwindet, wird der Deinitialisierer sofort aufgerufen. Kein Garbage Collector wartet — der Speicher wird sofort freigegeben.

ARC vs Garbage Collection: Hauptunterschiede

ARC und Garbage Collection lösen dasselbe Problem — automatische Speicherverwaltung — aber mit grundlegend unterschiedlichen Ansätzen. Die Wahl zwischen ihnen bestimmt die Spracharchitektur: Swift (ARC) vs Java/Go (GC). Betrachten wir die Hauptunterschiede.

MerkmalARC (Swift/ObjC)GC (Java/Go)
FreigabezeitpunktDeterministisch: sofort bei Zähler-NullNicht-deterministisch: beim nächsten Sammelzyklus
AusführungspausenKeine (retain/release zur Compile-Zeit eingefügt)Stop-The-World-Pausen (2–200 ms)
OverheadZählererhöhung/-senkung bei jeder ReferenzObjektdiagramm-Durchlauf, Markieren, Sweepen
ProblemeRetain Cycle (manuelle Auflösung)Heap-Fragmentierung, Lecks durch vergessene Referenzen
Zusätzlicher ThreadNicht erforderlichGarbage-Collector-Thread erforderlich

Der entscheidende Kompromiss: ARC bietet vorhersagbare Objektlebensdauern und keine Pausen, erfordert aber vom Entwickler Verständnis für retain cycles und die richtige Wahl von weak/unowned. GC befreit den Entwickler von diesen Sorgen, allerdings auf Kosten nicht-deterministischer Pausen und eines zusätzlichen Threads.

Strong, Weak und Unowned: Referenzqualifikatoren in ARC

ARC definiert drei Arten von Referenzqualifikatoren, die jeweils den Zähler und den Lebenszyklus des Objekts unterschiedlich beeinflussen. Die Wahl des richtigen Qualifikators ist die Grundlage für sichere Speicherverwaltung in Swift.

Strong

Strong ist der Standardqualifikator. Jede strong-Referenz erhöht den retain count des Objekts um 1. Solange mindestens eine strong-Referenz existiert, bleibt das Objekt aktiv. Alle Klasseneigenschaften und lokalen Variablen in Swift sind standardmäßig strong. Strong-Referenzen erzeugen eine Besitzerbeziehung: Objekt A besitzt Objekt B.

Weak

Weak ist eine Referenz, die den retain count nicht erhöht. Ein Objekt kann freigegeben werden, selbst wenn eine weak-Referenz darauf zeigt. Nach der Freigabe wird die weak-Referenz automatisch auf nil gesetzt. Weak-Referenzen werden immer als var mit optionalem Typ (?) deklariert. Sie werden verwendet, um retain cycles zu durchbrechen, insbesondere im Delegate-Muster.

Unowned

Unowned ist eine nicht-besitzende Referenz, die wie weak den retain count nicht erhöht. Eine unowned-Referenz wird jedoch nach der Freigabe nicht auf nil gesetzt — der Zugriff auf ein freigegebenes Objekt verursacht einen Absturz. Unowned wird verwendet, wenn garantiert ist, dass das Objekt mindestens so lange lebt wie das referenzierende Objekt. Typische Anwendungsfälle sind Closures und Eltern-Kind-Beziehungen mit garantierter Lebensdauer.

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

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — besitzt nicht
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card \(number) deallocated") }
}

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

Hier verwendet CreditCard eine unowned-Referenz auf Customer. Customer besitzt die Karte (strong), und die Karte besitzt den Kunden nicht (unowned). Wenn Customer freigegeben wird, werden beide Objekte freigegeben — es entsteht kein retain cycle. Wäre card.customer strong, würde der Zyklus die Freigabe blockieren.

Häufige ARC-Probleme und ihre Lösungen

Trotz der Automatisierung ist ARC kein Allheilmittel. Entwickler stoßen auf mehrere typische Probleme, die ein Verständnis des internen Speicherverwaltungsmechanismus erfordern.

Retain Cycle in Closures

Closures in Swift erfassen externe Variablen per strong-Referenz. Wenn eine Closure einer Klasseneigenschaft zugewiesen wird und self erfasst, entsteht ein retain cycle: die Klasse hält die Closure, die Closure hält self. Die Lösung ist eine Capture-Liste mit weak oder 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() { }
}

Die Capture-Liste [weak self] erzeugt eine weak-Referenz auf self innerhalb der Closure. Dies durchbricht den potenziellen retain cycle. Guard let self stellt sicher, dass das Objekt vor der Ausführung des Codes noch lebt. weak self ist die Standardpraxis für asynchrone Closures in Swift.

Retain/Release-Leistung

Obwohl retain/release leichte Operationen sind, verursachen häufige Zählererhöhungen/-senkungen in heißen Schleifen Overhead. In Swift 5.9+ verwendet der Compiler eine Optimierung, die redundante retain/release entfernt, wenn der Analysator die Sicherheit nachweist. In Objective-C können retain/release jedoch in hochbelasteten Szenarien mit Millionen von Aufrufen pro Sekunde immer noch ein Engpass sein.

Autorelease Pool

Autorelease Pool ist ein Mechanismus zur verzögerten Freigabe, der in Objective-C und einigen Swift-Szenarien verwendet wird. Objekte werden in den Pool gelegt und erhalten release, wenn der Pool entleert wird. In Schleifen mit vielen temporären Objekten (z.B. JSON-Parsing) reduziert die Erstellung eines eigenen autoreleasepool den Spitzen-Speicherverbrauch.

Häufig gestellte Fragen

Wie unterscheidet sich ARC von der manuellen Speicherverwaltung (MRR)?

Bei der manuellen Verwaltung (MRR) rief der Entwickler explizit retain, release und autorelease auf. ARC fügt diese Aufrufe automatisch zur Compile-Zeit ein, wodurch das Risiko von double-free, Lecks durch vergessenes release und Fehlern beim retain/release-Gleichgewicht entfällt.

Kann ARC mit C/C++-Code arbeiten?

ARC verwaltet nur Objective-C-Objekte und Swift-Klassen. Für C/C++-Strukturen und -Zeiger gilt ARC nicht — diese Objekte werden manuell oder über C++-Smart-Pointer (shared_ptr, unique_ptr) verwaltet. Core-Foundation-Objekte (CFString, CGColor) fallen ebenfalls nicht unter ARC.

Wann weak und wann unowned verwenden?

weak — wenn das Objekt vor dem referenzierenden Objekt freigegeben werden kann (Delegaten, asynchrone Closures). unowned — wenn garantiert ist, dass das Objekt mindestens so lange lebt wie das referenzierende Objekt (Eltern-Kind, bei dem das Kind ohne das Elternteil nicht existieren kann). Bei Unsicherheit weak wählen.

Was sind existenzielle Typen und wie beeinflussen sie ARC?

Existenzielle Typen (Protokoll als Typ) in Swift verpacken den Wert in einen speziellen Container (existential container). Dies erhöht die Anzahl der retain/release an Protokollgrenzen. In Swift 5.7+ reduzieren opaque result types und some-Parameter den Overhead, indem sie den Container eliminieren.

Wie überprüfe ich den retain count in Swift?

Es gibt keine direkte API zum Lesen des retain count in Swift — dies gilt als Implementierungsdetail. Zur Diagnose verwenden Sie Instruments (Allocations, Leaks) oder den Memory Debugger in Xcode. Diese Tools zeigen die Anzahl der lebenden Klasseninstanzen und die Halteketten.

Zusammenfassung

  • ARC — compilerbasiertes Speicherverwaltungssystem für Swift und Objective-C, das durch Referenzzählung funktioniert
  • Prinzip — jedes Objekt hat einen retain count; bei Null wird das Objekt sofort und deterministisch freigegeben
  • Unterschied zu GC — ARC arbeitet ohne Hintergrundthread oder Stop-The-World-Pausen, erfordert aber Kontrolle von retain cycles
  • Strong — erhöht den Zähler; weak und unowned erhöhen ihn nicht, aber unowned wird bei Freigabe nicht nil
  • Closures — die Hauptursache für retain cycles in Swift; [weak self] Capture-Liste ist die Standardlösung
  • Autorelease Pool — Mechanismus zur verzögerten Freigabe für temporäre Objekte in Schleifen und benutzerdefinierten Szenarien
  • Diagnose — Xcode Memory Debugger, Instruments und LeakCanary (über ObjC-Brücke) zur Fehlersuche

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

Projekt besprechen

Lesen Sie auch