Strong Reference (silná reference) — je standardní mechanismus správy paměti, při kterém objekt zůstává v paměti, dokud na něj ukazuje alespoň jedna aktivní reference. Na rozdíl od slabých referencí silná reference zvyšuje čítač referencí objektu a zabraňuje jeho automatickému uvolnění. Podle Apple Developer Documentation ARC automaticky spravuje životnost objektů ve Swiftu a Objective-C. Porozumění fungování silných referencí je klíčové pro prevenci úniků paměti a cyklických závislostí v mobilních aplikacích.
Hlavní body
Strong Reference — je typ reference na objekt, která zabraňuje jeho zničení garbage collectorem nebo systémem správy paměti. Dokud existuje alespoň jedna silná reference na objekt, paměť pod ním se neuvolňuje. To je základní mechanismus, na kterém jsou postaveny ARC ve Swiftu a Objective-C a garbage collection v Javě a Kotlinu.
Koncepce silné reference je fundamentální pro všechny jazyky s automatickou správou paměti. V systémech s ARC každá silná reference zvyšuje čítač referencí objektu. Když čítač klesne na nulu, objekt je okamžitě uvolněn. V Javě a Kotlinu s garbage collectorem silná reference zaručuje, že objekt je dosažitelný a nebude GC shromážděn.
Podle údajů WWDC 2021 je přibližně 35% úniků paměti v iOS aplikacích spojeno s nesprávným použitím silných referencí a retenčními cykly. Ve vývoji pro Android jsou úniky přes implicitní strong reference v闭eních a callbackách druhou nejčastější příčinou problémů s pamětí po Context Leaku.
Pro efektivní práci s pamětí je nutné rozumět rozdílu mezi strong, weak a unowned referencemi a správně vybírat typ reference v závislosti na vlastnictví a životnosti objektů.
Před zavedením ARC vývojáři ručně volali retain a release pro každý objekt, což vedlo k četným chybám. ARC, představený Apple v roce 2011 s vydáním LLVM 3.0, tento proces automatizoval analýzou grafu vlastnictví ve fázi kompilace. Kompilátor sám vkládá volání retain, release a autorelease na správná místa.
Podle Clang Static Analyzer snížilo zavedení ARC počet chyb souvisejících s pamětí v iOS aplikacích o 70%. Pro vývojáře to znamená, že správa paměti se stala bezpečnější, ale zároveň vznikla potřeba rozumět tomu, jak silné reference fungují pod kapotou — aby se předešlo retain cycles.
V Kotlinu a Javě roli ARC zastává garbage collector, ale princip silné reference zůstává stejný: GC Roots — to jsou vstupní body, přes které jsou objekty drženy silnými referencemi. Dokud je objekt dosažitelný přes řetězec silných referencí od GC Root, nebude shromážděn.
ARC (Automatic Reference Counting) funguje na principu počítání referencí pro každý objekt na haldě. Když je vytvořena nová silná reference na objekt, čítač se zvyšuje (retain). Když je reference zničena nebo přepsána, čítač se snižuje (release). Když čítač dosáhne nuly, objekt je okamžitě odstraněn z paměti.
Podívejme se na příklad ve Swiftu. Při vytváření instance třídy ARC alokuje paměť a nastaví retain count na 1. Každé nové přiřazení jiné proměnné zvyšuje čítač. Když proměnná opustí rozsah platnosti, čítač se snižuje:
class ProfileViewController {
var nameLabel: String?
var avatarImage: UIImage?
func loadProfile() {
// retain count = 1 pro novou instanci
let user = User(name: "Ivan")
// retain count = 2 po přiřazení nameLabel
nameLabel = user.name
// výstup z metody — user opouští rozsah, retain count = 1
}
}
V tomto kódu ARC zaručuje, že objekt User zůstává v paměti, dokud na něj existuje alespoň jedna silná reference. Když funkce loadProfile skončí, lokální proměnná user je zničena, ale nameLabel stále drží objekt. Paměť se uvolní teprve tehdy, když nameLabel přestane existovat nebo bude přepsána.
V Kotlinu je podobné chování zajištěno prostřednictvím GC Roots. Dokud existuje sledovatelný řetězec strong references od kořene garbage collectoru (například statické pole nebo aktivní vlákno), objekt zůstává v paměti. Rozdíl je v tom, že GC neuvolňuje paměť okamžitě — děje se tak asynchronně po analýze dosažitelnosti.
V ARC dochází k uvolnění synchronně v okamžiku vynulování čítače. Ve Swiftu a Objective-C přesně víte, kdy bude objekt odstraněn. V Kotlinu a Javě je okamžik uvolnění nepředvídatelný, ale je kompenzován flexibilnějším schématem detekce cyklických závislostí na úrovni garbage collectoru.
Retain cycle (retenční cyklus) — situace, kdy dva nebo více objektů mají vzájemné silné reference na sebe. V důsledku toho jejich retain count nikdy neklesne na nulu a paměť se neuvolňuje, ani když objekty již aplikace nepotřebuje.
Klasický příklad: nadřazený view controller drží podřízený objekt silnou referencí a ten zase silnou referencí drží nadřazený. To je typické pro situace s delegáty, uzávěry a vnořenými lambda výrazy. Podle Instruments Leaks tvoří retain cycles až 60% všech úniků paměti v aplikacích používajících ARC.
class ParentViewController: UIViewController {
var child: ChildViewController?
func setupChild() {
child = ChildViewController()
// retain cycle: parent drží child, child drží parent přes closure
child?.onEvent = {
self.handleEvent()
}
}
func handleEvent() {}
}
Problém je v tom, že uzávěr onEvent zachycuje self (ParentViewController) silnou referencí a samotný ParentViewController drží child silnou referencí. Oba objekty nebudou nikdy uvolněny. Řešením je použití weak self v uzávěru k přerušení cyklu.
V Kotlinu vznikají podobné cykly při použití lambd zachycujících vnější objekty. Garbage collector JVM může časem takové cykly detekovat, ale pouze pokud jsou objekty nedosažitelné z GC Roots. Pokud je cyklus spojen s aktivním vláknem nebo UI kontextem, únik přetrvává po celou dobu životnosti aplikace.
Porozumění rozdílu mezi typy referencí — klíč k bezpečné správě paměti. Strong Reference zvyšuje retain count. Weak Reference nezvyšuje retain count a při uvolnění objektu se automaticky stává nil. Unowned Reference také nezvyšuje retain count, ale nevynuluje se — odkaz na ni po uvolnění způsobí crash.
| Typ reference | Retain count | Bezpečnost | Kdy použít |
|---|---|---|---|
| Strong | +1 | Bezpečné (výchozí) | Vlastnictví objektu, vztah parent → child |
| Weak | Nemění | Automatické vynulování (safe) | Delegáti, callback, zpětné reference |
| Unowned | Nemění | Riziko crash při pozdějším odkazu | Když objekt žije zaručeně déle než vlastník |
Výběr typu reference je dán vztahem vlastnictví. Pokud je objekt B součástí A a nemůže bez něj existovat — použijte Strong. Pokud B může existovat nezávisle a odkazuje na A pro oznámení — použijte Weak. Unowned se používá zřídka — pouze když životnost podřízeného objektu striktně nepřesahuje životnost rodiče.
Apple Developer Documentation doporučuje: standardně používejte strong pro všechny vztahy vlastnictví. Pokud je třeba se vyhnout retain cycle — určete, která reference by měla být slabá. Obvykle je to zpětná reference v hierarchii (child → parent). V Kotlinu má podobnou roli WeakReference z java.lang.ref, který se používá pro cache a vzor pozorovatele.
Detekce retain cycles — první krok. Druhý — jejich správné odstranění. Hlavním nástrojem boje proti cyklům silných referencí je nahrazení jedné z referencí weak nebo unowned. V jazycích s garbage collection se dodatečně používá WeakReference s ruční kontrolou null před každým přístupem.
Ve Swiftu a Objective-C je nejčastější opravou přidání [weak self] do uzávěrů. To zaručuje, že uzávěr nedrží objekt po jeho uvolnění. V Kotlinu se pro podobné účely používá obálka WeakReference nebo explicitní čištění reference v onDestroy.
class NetworkService {
func fetchData(completion: @escaping (Data?) -> Void) {
// zachycení přes weak self — retain cycle vyloučen
URLSession.shared.dataTask(
with: URL(string: "https://api.example.com")!
) { [weak self] data, response, error in
guard let self else { return }
completion(data)
}.resume()
}
}
V tomto příkladu [weak self] zaručuje, že NetworkService nebude držen uzávěrem poté, co již není potřeba. Pokud byl self uvolněn před dokončením požadavku — guard let self else { return } opouští uzávěr bez volání completion.
Pro diagnostiku retain cycles použijte Instruments Leaks pro iOS nebo Android Profiler + LeakCanary pro Android. Tyto nástroje ukazují přesný graf zadržení a ukazují, která silná reference brání uvolnění objektu. Pravidelné profilování paměti by mělo být součástí CI/CD pipeline každého mobilního projektu.
Swift a Kotlin používají zásadně odlišné mechanismy správy paměti, ale koncepce silné reference je přítomna v obou. Ve Swiftu se používá ARC se synchronním uvolněním při retain count = 0. V Kotlinu se používá sledovací GC, který asynchronně čistí nedosažitelné objekty.
| Parametr | Swift (ARC) | Kotlin (JVM GC) |
|---|---|---|
| Mechanismus | Počítání referencí (retain count) | Sledování dosažitelnosti (GC Roots) |
| Uvolnění | Synchromí (při vynulování čítače) | Asynchronní (cyklem GC) |
| Retain cycle | Není detekován automaticky | GC může detekovat, ale ne okamžitě |
| Weak ref | weak (automatické vynulování) | WeakReference (ruční kontrola) |
Hlavní praktický rozdíl: ve Swiftu je retain cycle — zaručený únik. V Kotlinu může GC cyklus přerušit, pokud jsou objekty nedosažitelné z kořene, ale životnost uniklých objektů zůstává nepředvídatelná. Proto je v obou jazycích nejlepší strategií vyhýbat se cyklům silných referencí ve fázi návrhu.
Pro Swift používejte weak v delegátních vzorech a uzávěrech. Pro Kotlin — WeakReference nebo Lifecycle-aware komponenty, které automaticky čistí reference při zničení vlastníka. V obou přístupech je cíl stejný — vyloučit silné reference tam, kde vytvářejí nepřerušitelný řetězec zadržení.
Často kladené otázky
Strong Reference zvyšuje retain count objektu a zabraňuje jeho uvolnění, dokud reference existuje. Weak Reference nemění retain count a automaticky se vynuluje, když je objekt odstraněn z paměti. Silné reference se používají pro vlastnictví, slabé — pro zpětná spojení a delegáty.
Retain cycle — vzájemná blokace, při které dva objekty drží jeden druhého silnými referencemi. Jejich retain count nikdy neklesne na nulu, paměť se neuvolňuje. To vede k úniku paměti: objekty zůstávají na haldě navždy, aplikace spotřebovává stále více zdrojů a nakonec spadne s OutOfMemory.
Použijte Instruments Leaks z Xcode — spusťte profilování se šablonou Leaks, proveďte scénář v aplikaci a zkontrolujte indikátory úniků. Pro přesnou diagnostiku přepněte na kartu Cycles & Roots — zobrazí graf vzájemných strong reference tvořících nepřerušitelný cyklus.
Unowned použijte, když životnost podřízeného objektu rozhodně nepřesahuje životnost rodiče — například při vázání objektu na striktně definovaný rozsah. V případě pochybností použijte Weak, protože odkaz na uvolněný unowned způsobí crash aplikace.
Nepřímo — ano. Každý retain a release v ARC je atomická operace s režií. Při velkém počtu objektů v cyklech to může ovlivnit výkon. Hlavním problémem však není rychlost ARC, ale úniky paměti kvůli nesprávně zvolenému typu reference.
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é