Automatic Reference Counting (ARC) — systém správy paměti v Swift a Objective-C, který automaticky počítá počet referencí na každý objekt a uvolňuje jej, když počítadlo dosáhne nuly. Podle Apple Swift Documentation, 2026 je ARC vestavěno do překladače a pracuje ve fázi překladu, vkládající volání retain/release na správná místa. Na rozdíl od Garbage Collection ARC nevyžaduje samostatné vlákno kolektoru a nevytváří pauzy během provádění aplikace.
Hlavní body
ARC (Automatic Reference Counting) — je mechanismus správy paměti na úrovni překladače, představený společností Apple v Xcode 4.2 (2011) pro Objective-C a zděděný Swiftem. Na rozdíl od ruční správy paměti (Manual Retain-Release, MRR) ARC zcela automatizuje volání retain, release a autorelease, vkládá je ve fázi překladu bez účasti vývojáře.
ARC není garbage collector. Je to statická analýza s dynamickým vkládáním kódu: překladač analyzuje životnost objektů a umísťuje retain/release v bodech, kde jsou objekty vytvářeny, kopírovány nebo opouštějí rozsah platnosti. Výsledek — deterministické uvolňování paměti: objekt je smazán přesně v okamžiku, kdy na něj již neodkazuje žádná reference, bez zpoždění a pauz.
Podle WWDC 2011 Session 323 snížil přechod z MRR na ARC počet crash-bugů souvisejících s pamětí o 70 % v aplikacích Apple. Vývojáři přestali ručně vyvažovat retain/release, což eliminovalo celou třídu úniků a double-free chyb.
Každý objekt v paměti má počítadlo referencí (retain count). Při vytvoření objektu je počítadlo nastaveno na 1. Když nová strong-reference ukazuje na objekt — počítadlo se zvyšuje (retain). Když strong-reference zmizí — počítadlo se snižuje (release). Při dosažení nuly je objekt okamžitě dealokován.
Swift překladač nevkládá retain/release při každém přiřazení — používá statickou analýzu pro optimalizaci. Například, pokud je objekt zaručeně nepoužit po přenosu, může překladač přeskočit zbytečný release/retain. Tato optimalizace se nazývá ARC Optimisation.
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) inicializován (retain count: 1)")
}
deinit {
print("\(name) dealokován")
}
}
func testARC() {
let p = Person(name: "Alice") // retain count = 1
let q = p // retain count = 2
// q opouští rozsah
// retain count = 1
// p opouští rozsah
// retain count = 0 → deinit
}
V tomto příkladu je vidět, jak ARC spravuje počítadlo: při přiřazení q = p se počítadlo zvyšuje, při opuštění rozsahu q — snižuje. Když poslední strong-reference zmizí, deinicializátor je okamžitě volán. Žádný garbage collector nečeká — paměť je uvolněna okamžitě.
ARC a Garbage Collection řeší stejný úkol — automatickou správu paměti — ale zásadně odlišnými přístupy. Volba mezi nimi určuje architekturu jazyka: Swift (ARC) vs Java/Go (GC). Podíváme se na hlavní rozdíly.
| Charakteristika | ARC (Swift/ObjC) | GC (Java/Go) |
|---|---|---|
| Okamžik uvolnění | Deterministický: ihned při vynulování počítadla | Nedeterministický: při následném sběru |
| Pauzy provádění | Žádné (vložení retain/release ve fázi překladu) | Existují pauzy Stop-The-World (2–200 ms) |
| Režie | Zvyšování/snižování počítadla při každé referenci | Průchod grafem objektů, značení, uvolňování |
| Problémy | Retain Cycle (ruční řešení) | Fragmentace haldy, úniky u zapomenutých referencí |
| Další vlákno | Není vyžadováno | Vyžaduje vlákno garbage collectoru |
Klíčový kompromis: ARC poskytuje předvídatelnou životnost objektů a nulové pauzy, ale vyžaduje od vývojáře pochopení retain cycle a správnou volbu weak/unowned. GC od těchto starostí osvobozuje, ale za cenu nedeterministických pauz a dalšího vlákna.
ARC definuje tři typy kvalifikátorů referencí, z nichž každý ovlivňuje počítadlo a životní cyklus objektu jinak. Správná volba kvalifikátoru je základem bezpečné práce s pamětí ve Swift.
Strong — výchozí kvalifikátor. Každá strong-reference zvyšuje retain count objektu o 1. Dokud existuje alespoň jedna strong-reference, objekt žije. Všechny vlastnosti tříd a lokální proměnné ve Swift jsou ve výchozím nastavení strong. Strong-reference vytvářejí vztah vlastnictví: objekt A vlastní objekt B.
Weak — reference, která nezvyšuje retain count. Objekt může být dealokován, i když na něj weak-reference ukazuje. Po dealokaci je weak-reference automaticky nastavena na nil. Weak-reference jsou vždy deklarovány jako var s volitelným typem (?). Používají se k přerušení retain cycle, zejména v patternu delegate.
Unowned — nevlastnící reference, která stejně jako weak nezvyšuje retain count. Nicméně unowned-reference nen\u00edstavena na nil po dealokaci — přístup k uvolněnému objektu způsobí crash. Unowned se používá, když je zaručeno, že objekt žije alespoň tak dlouho jako odkazující objekt. Typický scénář — uzávěry (closures) a vztahy rodič-dítě se zaručenou životností.
class Customer {
let name: String
var card: CreditCard? // strong
init(name: String) { self.name = name }
deinit { print("\(name) dealokován") }
}
class CreditCard {
let number: String
unowned let customer: Customer // unowned — nevlastní
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Karta \(number) dealokována") }
}
var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer a CreditCard oba uvolněni — žádný retain cycle
Zde CreditCard používá unowned-reference na Customer. Customer vlastní kartu (strong) a karta nevlastní zákazníka (unowned). Když se Customer stane nil, oba objekty jsou uvolněny — retain cycle nevzniká. Pokud by card.customer bylo strong, cyklus by blokoval uvolnění.
Navzdory automatizaci není ARC všelékem. Vývojáři se setkávají s několika typickými problémy, které vyžadují pochopení vnitřního mechanismu správy paměti.
Uzávěry (closures) ve Swift zachycují vnější proměnné prostřednictvím strong-reference. Pokud je uzávěra přiřazena vlastnosti třídy a zachycuje self — vzniká retain cycle: třída drží uzávěru, uzávěra drží self. Řešení — capture list s weak nebo 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] vytváří weak-referenci na self uvnitř uzávěry. To přeruší potenciální retain cycle. Guard let self zaručuje, že objekt je živý před provedením kódu. weak self — standardní praxe pro asynchronní uzávěry ve Swift.
Ačkoliv jsou retain/release lehké operace, časté zvyšování/snižování počítadla v horkých smyčkách vytváří režii. Ve Swift 5.9+ používá překladač optimalizaci, při které jsou zbytečné retain/release odstraněny, pokud analyzátor prokáže bezpečnost. Nicméně v Objective-C mohou být retain/release stále úzkým místem ve vysoce zatížených scénářích s miliony volání za sekundu.
Autorelease Pool — mechanismus odloženého release, používaný v Objective-C a některých scénářích Swift. Objekty jsou umístěny do poolu a obdrží release při drain poolu. Ve smyčkách s velkým počtem dočasných objektů (např. parsování JSON) sníží vytvoření vlastního autoreleasepoolu špičkovou spotřebu operační paměti.
Často kladené otázky
Při ruční správě (MRR) vývojář explicitně volal retain, release a autorelease. ARC vkládá tato volání automaticky ve fázi překladu, čímž eliminuje riziko double-free, úniky kvůli zapomenutému release a chyby vyvážení retain/release.
ARC spravuje pouze objekty Objective-C a třídy Swift. Pro struktury a ukazatele C/C++ se ARC nepoužívá — tyto objekty jsou spravovány ručně nebo pomocí chytrých ukazatelů C++ (shared_ptr, unique_ptr). Objekty Core Foundation (CFString, CGColor) také nespadají pod ARC.
weak — když může být objekt dealokován dříve než odkazující objekt (delegáty, asynchronní uzávěry). unowned — když je zaručeno, že objekt žije alespoň tak dlouho jako odkazující objekt (rodič-dítě, kde dítě nemůže existovat bez rodiče). Pokud si nejste jisti — zvolte weak.
Existenční typy (protocol as type) ve Swift zabalí hodnotu do speciálního kontejneru (existential container). To zvyšuje počet retain/release na hranicích protokolů. Ve Swift 5.7+ snižují opaque result types a some parametry režii odstraněním kontejneru.
Neexistuje přímé API pro čtení retain count ve Swift — je to považováno za detail implementace. Pro diagnostiku použijte Instruments (Allocations, Leaks) nebo Memory Debugger v Xcode. Tyto nástroje ukazují počet živých instancí třídy a řetězce držení.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také