Memory Graph: co to je, graf objektů a detekce cyklických referencí

Autor: IT Sectr Publikováno: 2026-05-07 Doba čtení: 10 min

Memory Graph — vizuální nástroj Xcode Debug Navigator, který zobrazuje graf objektů v operační paměti aplikace s jejich vzájemnými referencemi. Na rozdíl od heap dumpu, Memory Graph ukazuje nejen seznam objektů, ale orientovaný graf referencí, kde každý uzel je objekt a každá hrana je reference (strong, weak, unowned). Podle údajů Apple WWDC 2018 nástroj umožňuje vizuálně detekovat retain cycles a úniky paměti během několika sekund, bez nutnosti analýzy surových dat heap dumpu.

Hlavní body

  • Memory Graph — vizuální graf objektů v paměti Xcode, ukazující reference mezi objekty v reálném čase.
  • Retain cycle je detekován uzavřeným konturem v grafu — dva nebo více objektů na sebe odkazují silnými referencemi.
  • Backtrace pro každou hranu grafu ukazuje, kde a kdy byla reference nastavena, což usnadňuje nalezení zdroje úniku.
  • Filtrování podle názvu třídy a typu reference (strong/weak) umožňuje rychle izolovat problematické objekty.
  • Integrace s Memory Reportem v Xcode umožňuje sledovat změny spotřeby paměti v reálném čase.

Co je Memory Graph a jak funguje

Memory Graph — je součást Xcode Debug Navigator (objevil se v Xcode 10, WWDC 2018), který vytváří orientovaný graf všech objektů v paměti laděného procesu. Každý uzel grafu je instance třídy (Objective-C nebo Swift), každá hrana je reference na jiný objekt. Barva hrany udává typ reference: modrá — strong, zelená — weak, šedá — unowned. Graf je vytvářen na základě dat LLDB a Objective-C runtime, proto pro správnou funkci musí být aplikace zkompilována v konfiguraci Debug s povolenými symboly.

Princip fungování: když je aplikace pozastavena na breakpointu, Xcode prostřednictvím LLDB požádá runtime o všechny živé objekty a jejich reference. LLDB používá objc_getClassList a iteraci přes alokační regiony k vytvoření kompletního grafu. Na ARM64 (Apple Silicon) se dodatečně používají hardwarové prostředky pro sledování alokací bez zpomalení. Doba vytváření grafu závisí na velikosti haldy: pro typickou iOS aplikaci (50–200 MB) se graf vytvoří za 1–3 sekundy.

Podle údajů Apple je Memory Graph jediným nástrojem, který dokáže vizualizovat retain cycles bez úpravy kódu nebo přidání instrumentace. Na rozdíl od Instruments Leaks funguje Memory Graph v reálném čase uvnitř Xcode a nevyžaduje samostatné spuštění profilovače. To z něj dělá nástroj první volby pro rychlou diagnostiku úniků paměti v procesu vývoje.

Čím se Memory Graph liší od heap dumpu

Heap dump poskytuje tabulku všech objektů s čísly (shallow size, retained size) — je optimální pro kvantitativní analýzu. Memory Graph poskytuje vizuální obraz spojení — je optimální pro hledání cyklických referencí. Nástroje se doplňují: nejprve Memory Graph pro rychlou detekci retain cycles, poté heap dump přes Instruments Allocations pro přesné měření retained size. Podle zkušeností objc.io kombinace obou metod pokrývá 95% scénářů úniků paměti.

Detekce retain cycles pomocí Memory Graph

Retain cycle — situace, kdy dva nebo více objektů drží jeden druhého silnými referencemi, čímž vytváří uzavřený kontur. ARC nemůže takový kontur uvolnit, protože retain count každého objektu nikdy nedosáhne nuly. Klasický příklad: ViewController a View, kde View má strong reference na closure, který zachytává self (ViewController). Memory Graph zobrazuje takové kontury jako prstence (cykly) a zvýrazňuje je pro rychlou identifikaci.

Když Xcode detekuje retain cycle, zvýrazní ho oranžovým konturem a zobrazí varování v Debug Navigator. Kliknutím na cyklus uvidíte řetězec referencí tvořící uzavřený kontur. Vývojáři zbývá určit, která ze silných hran by měla být slabá — obvykle je to reference z podřízeného objektu na nadřazený (např. delegate nebo closure).

swift
class ViewController: UIViewController {
    let service = DataService()

    override func viewDidLoad() {
        super.viewDidLoad()
        // ❌ Retain cycle: ViewController → služba → closure → ViewController
        service.fetchData { self.updateUI($0) }
    }

    func updateUI(_ data: Data) {}
}

class DataService {
    var completion: ((Data) -> Void)?

    func fetchData(handler: @escaping (Data) -> Void) {
        self.completion = handler
    }
}

V Memory Graph uvidíte trojúhelník: ViewController → DataService → closure → ViewController. Řešením je slabé zachycení self: [weak self]. Po opravě Memory Graph ukáže zelenou hranu od closure k ViewController a retain cycle zmizí.

swift
// Opravený kód — slabé zachycení self
service.fetchData { [weak self] data in
    guard let self else { return }
    self.updateUI(data)
}

Rozhraní Memory Graph Debugger v Xcode

Rozhraní Memory Graph Debugger se skládá ze tří panelů: levý — seznam všech živých objektů (seskupených podle tříd) s počtem instancí; střední — vizuální graf s přetahovatelnými uzly; pravý — inspektor vybraného objektu nebo hrany. V seznamu objektů se zobrazuje: ikona třídy, počet instancí v paměti, celkový retained size a procento celé haldy. Filtrování podle názvu třídy podporuje regulární výrazy.

Navigace v grafu

Uzly grafu lze přetahovat pro zlepšení čitelnosti. Dvojité kliknutí na uzel otevře podrobné informace o objektu: všechny jeho vlastnosti s typy a hodnotami, zásobník volání (backtrace) pro každou vlastnost a historii retain/release. Backtrace — klíčová funkce: ukazuje, který přesně řádek kódu nastavil referenci na objekt. To umožňuje najít zdroj úniku bez ručního prohlížení celého kódu.

Pro složité grafy Xcode poskytuje automatické rozložení pomocí Layout → Hierarchical (hierarchické) nebo Cluster (shlukové). Hierarchické rozložení umísťuje kořenové objekty nahoru, podřízené dolů, což usnadňuje hledání řetězců. Shlukové rozložení seskupuje související objekty do shluků, což je vhodné, když graf obsahuje několik izolovaných skupin. Podle údajů Apple se pro většinu aplikací doporučuje hierarchické rozložení — je intuitivní a vyžaduje méně času pro vizuální analýzu.

lldb
// Příkazy LLDB, které Memory Graph používá pod kapotou
(lldb) script import lldb.macosx.heap
(lldb) script heap.find_variable("viewController")
0x600000c4b80: ViewController
(lldb) script heap.refs 0x600000c4b80
0x600000c4b80 -> 0x600003a4c00 (DataService)
    ivar: _service, offset: 16

Analýza grafu: hledání a odstraňování úniků

Systematický přístup k analýze Memory Graph zahrnuje několik fází. Fáze 1: spusťte aplikaci, proveďte scénář, který potenciálně způsobuje únik (otevřete/zavřete obrazovku, proveďte síťový požadavek). Fáze 2: stiskněte tlačítko Memory Graph v Debug Navigator — Xcode vytvoří graf. Fáze 3: zkontrolujte oranžová varování retain cycles v levém panelu. Fáze 4: pro podezřelé objekty použijte volbu Show only cycles — zobrazí se pouze uzly účastnící se cyklických referencí.

Použití backtrace pro nalezení zdroje

Když je retain cycle nalezen, klikněte na hranu cyklu a otevřete panel inspektoru. V sekci Backtrace je zobrazen zásobník volání v okamžiku, kdy byla tato reference nastavena. Například, pokud hrana vede od closure k self, backtrace ukáže, ve které metodě a na kterém řádku kódu byl closure vytvořen. To eliminuje potřebu hádat — okamžitě vidíte bod vytvoření problematické reference. Podle údajů WWDC Labs analýza backtrace zkracuje dobu diagnostiky retain cycle z 15–20 minut na 2–3 minuty.

swift
class ProfileViewController: UIViewController {
    var profileView: ProfileView!

    override func viewDidLoad() {
        super.viewDidLoad()
        profileView = ProfileView()
        // Memory Graph zde ukáže retain cycle
        profileView.onTap = { [unowned self] in
            // ⚠️ unowned může způsobit pád při nil self
            self.navigateToDetail()
        }
    }

    func navigateToDetail() { }
}

// ✅ Správně: [weak self] + guard let self
profileView.onTap = { [weak self] in
    guard let self else { return }
    self.navigateToDetail()
}

Filtrování nadbytečných objektů

Memory Graph může zobrazovat tisíce objektů, což ztěžuje hledání. Používejte filtry v levém panelu: zadejte název třídy (např. ProfileViewController), aby se zobrazily pouze instance této třídy. Poté vyberte instanci, která měla být uvolněna (pokud je obrazovka zavřená, ale objekt zůstal). Aplikujte Show Reachable From — zobrazí se pouze reference relevantní pro tento objekt, zbytek grafu bude skryt.

Praktické tipy pro použití Memory Graph

Zkušení vývojáři používají Memory Graph nejen k hledání úniků, ale také k proaktivní kontrole paměti. Kontrolujte Memory Graph po každé větší změně architektury — přidání nového delegáta, closure nebo odběru NotificationCenter. Stačí provést typický scénář a ujistit se, že objekty jsou správně uvolňovány a retain cycles chybí. To trvá 2–3 minuty, ale předchází hodinám pozdějšího ladění.

Kombinace s Memory Report

Memory Report v Xcode (záložka Debug Navigator) zobrazuje graf spotřeby paměti v reálném čase. Používejte jej spolu s Memory Graph: otevřete Memory Graph při prudkém nárůstu spotřeby. Například při rolování dlouhého seznamu s buňkami načítajícími obrázky Memory Graph ukáže, které objekty se vytvářejí a které se uvolňují. Pokud počet objektů roste bez poklesu — jedná se o potenciální únik, viditelný dříve, než vede k pádu. Podle údajů Apple je kombinace Memory Graph + Memory Report doporučeným workflow pro všechny iOS vývojáře od Xcode 12.

objective-c
// Příklad úniku v Objective-C přes delegaci
@interface DownloadManager : NSObject
@property (strong) id delegate; // ❌ Mělo by být weak!
@end

@implementation DownloadManager
// Memory Graph ukáže retain cycle:
// ViewController → DownloadManager.delegate → ViewController
@end

// Oprava: weak property
@property (weak) id delegate;

Profilování closures

Zvláštní pozornost věnujte closures — nejčastějšímu zdroji retain cycles ve Swiftu. Při zachycení self uvnitř closure, který je uložen jako vlastnost objektu, vzniká klasický cyklus. Memory Graph to zobrazí jako closure (uzel se symbolem {}), spojený modrými hranami se zachycenými objekty. Pravidelně kontrolujte všechny closures, zejména ty použité v asynchronních voláních, GCD, Combine a SwiftUI. Podle statistik Point-Free 90% úniků v Swift projektech souvisí s closures zachycujícími self.

Často kladené otázky

Funguje Memory Graph pouze pro Objective-C nebo také pro Swift?

Memory Graph funguje pro oba jazyky, protože používá Objective-C runtime. Swift objekty kompatibilní s ObjC (dědící z NSObject, označené @objc) jsou zobrazeny plně. Čisté Swift struktury a třídy bez ObjC mostu jsou viditelné omezeně.

Proč Memory Graph nezobrazuje některé objekty?

Objekty musí být registrovány v Objective-C runtime. Swift value types (struct, enum) se nezobrazují. Ujistěte se, že třída dědí z NSObject nebo používá atribut @objc pro viditelnost v Memory Graph.

Jak interpretovat barvy hran v grafu?

Modrá — strong reference, drží objekt. Zelená — weak reference, neovlivňuje životní cyklus. Šedá — unowned reference. Retain cycle se tvoří pouze z modrých hran.

Zpomaluje Memory Graph aplikaci?

Vytváření grafu pozastaví aplikaci na 1–3 sekundy a může dočasně zvýšit spotřebu paměti Xcode o 200–500 MB. Samotná aplikace se nezpomaluje, protože inspekce probíhá během pauzy na breakpointu.

Lze Memory Graph exportovat pro analýzu?

Xcode nepodporuje přímý export grafu. Použijte snímek obrazovky pro dokumentaci nebo skript lldb heap.find_variable pro programové získání dat. Pro podrobnou analýzu použijte Instruments Allocations s heap dumpem.

Shrnutí

  • Memory Graph — vizuální nástroj Xcode pro zobrazení grafu objektů v paměti s jejich referencemi.
  • Retain cycle je zobrazen jako uzavřený kontur z modrých (strong) hran — Xcode jej zvýrazní oranžově.
  • Backtrace pro každou hranu grafu ukazuje přesné místo v kódu, kde byla vytvořena problematická reference.
  • Filtrování podle tříd a typu referencí umožňuje izolovat úniky v grafu s tisíci objektů.
  • Closures — hlavní zdroj retain cycles ve Swiftu, Memory Graph je zobrazuje jako uzly {}.
  • Weak a unowned — řešení pro přerušení cyklu, ale weak je preferován kvůli bezpečnosti při nil.
  • Pravidelná kontrola Memory Graph po změnách architektury zabraňuje regresi paměti v projektu.

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

Prodiskutovat projekt

Přečtěte si také