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) 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.
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.
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 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.
| Merkmal | ARC (Swift/ObjC) | GC (Java/Go) |
|---|---|---|
| Freigabezeitpunkt | Deterministisch: sofort bei Zähler-Null | Nicht-deterministisch: beim nächsten Sammelzyklus |
| Ausführungspausen | Keine (retain/release zur Compile-Zeit eingefügt) | Stop-The-World-Pausen (2–200 ms) |
| Overhead | Zählererhöhung/-senkung bei jeder Referenz | Objektdiagramm-Durchlauf, Markieren, Sweepen |
| Probleme | Retain Cycle (manuelle Auflösung) | Heap-Fragmentierung, Lecks durch vergessene Referenzen |
| Zusätzlicher Thread | Nicht erforderlich | Garbage-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.
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 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 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 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.
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.
Trotz der Automatisierung ist ARC kein Allheilmittel. Entwickler stoßen auf mehrere typische Probleme, die ein Verständnis des internen Speicherverwaltungsmechanismus erfordern.
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.
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.
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 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
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.
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.
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.
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.
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
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.
Lesen Sie auch