Memory Graph: mi ez, objektumgráf és ciklikus hivatkozások észlelése

Szerző: IT Sectr Megjelenés: 2026-05-07 Olvasási idő: 10 perc

Memory Graph — az Xcode Debug Navigator vizuális eszköze, amely az alkalmazás operatív memóriájában lévő objektumok gráfját jeleníti meg azok kölcsönös hivatkozásaival. Ellentétben a heap dumppal, a Memory Graph nem csak egy objektumlistát mutat, hanem egy irányított hivatkozásgráfot, ahol minden csomópont egy objektum, és minden él egy hivatkozás (strong, weak, unowned). A Apple WWDC 2018 szerint az eszköz másodpercek alatt lehetővé teszi a retain cycles és a memóriaszivárgások vizuális észlelését, anélkül hogy elemezni kellene a heap dump nyers adatait.

Főbb pontok

  • Memory Graph — az Xcode memóriájában lévő objektumok vizuális gráfja, amely valós időben mutatja az objektumok közötti hivatkozásokat.
  • Retain cycle egy zárt kontúrral észlelhető a gráfban — két vagy több objektum erős hivatkozásokkal hivatkozik egymásra.
  • Backtrace a gráf minden éléhez megmutatja, hol és mikor lett beállítva a hivatkozás, megkönnyítve a szivárgás forrásának megtalálását.
  • Szűrés osztálynév és hivatkozástípus (strong/weak) szerint lehetővé teszi a problémás objektumok gyors elkülönítését.
  • Integráció a Memory Reporttal az Xcode-ban lehetővé teszi a memóriafogyasztás változásának valós idejű követését.

Mi az a Memory Graph és hogyan működik

Memory Graph — az Xcode Debug Navigator összetevője (az Xcode 10-ben, WWDC 2018-ban jelent meg), amely a hibakeresés alatt álló folyamat memóriájában lévő összes objektum irányított gráfját építi fel. A gráf minden csomópontja egy osztálypéldány (Objective-C vagy Swift), minden éle egy hivatkozás egy másik objektumra. Az él színe jelzi a hivatkozás típusát: kék — strong, zöld — weak, szürke — unowned. A gráf az LLDB és az Objective-C runtime adatai alapján épül fel, ezért a helyes működéshez az alkalmazást Debug konfigurációban kell lefordítani bekapcsolt szimbólumokkal.

Működési elv: amikor az alkalmazás meg van állítva egy breakpointnál, az Xcode az LLDB-n keresztül lekéri a runtime-tól az összes élő objektumot és azok hivatkozásait. Az LLDB a objc_getClassList és a lefoglalási régiókon való iteráció segítségével építi fel a teljes gráfot. ARM64-en (Apple Silicon) további hardveres eszközök használatosak a lefoglalások nyomon követésére lassulás nélkül. A gráf felépítési ideje a heap méretétől függ: egy tipikus iOS-alkalmazásnál (50–200 MB) a gráf 1–3 másodperc alatt épül fel.

Az Apple szerint a Memory Graph az egyetlen eszköz, amely képes a retain cycles vizualizálására kódmódosítás vagy instrumentáció hozzáadása nélkül. Az Instruments Leaks-től eltérően a Memory Graph valós időben működik az Xcode-on belül, és nem igényli a profilozó külön elindítását. Ez teszi az első választás eszközévé a memóriaszivárgások gyors diagnosztizálásához a fejlesztési folyamat során.

Miben különbözik a Memory Graph a heap dumptól

Heap dump egy táblázatot ad az összes objektumról számokkal (shallow size, retained size) — mennyiségi elemzésre optimális. Memory Graph vizuális képet ad a kapcsolatokról — ciklikus hivatkozások keresésére optimális. Az eszközök kiegészítik egymást: először Memory Graph a retain cycles gyors észlelésére, majd heap dump az Instruments Allocations segítségével a retained size pontos mérésére. A objc.io tapasztalata szerint a két módszer kombinációja a memóriaszivárgási forgatókönyvek 95%-át lefedi.

Retain cycles észlelése Memory Graph segítségével

Retain cycle — olyan helyzet, amikor két vagy több objektum erős hivatkozásokkal tartja egymást, zárt kontúrt képezve. Az ARC nem tudja felszabadítani az ilyen kontúrt, mert minden objektum retain count-ja soha nem éri el a nullát. Klasszikus példa: ViewController és View, ahol a View-nak strong reference-e van egy closure-ra, amely megragadja a self-et (ViewController). A Memory Graph az ilyen kontúrokat gyűrűkként (ciklusokként) jeleníti meg, kiemelve azokat a gyors azonosításhoz.

Amikor az Xcode észlel egy retain cycle-t, narancssárga kontúrral emeli ki, és figyelmeztetést jelenít meg a Debug Navigatorban. A ciklusra kattintva látja a zárt kontúrt alkotó hivatkozási láncot. A fejlesztőnek kell meghatároznia, hogy melyik erős él legyen gyenge — általában ez a gyermek objektumtól a szülő felé mutató hivatkozás (pl. delegate vagy closure).

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

    override func viewDidLoad() {
        super.viewDidLoad()
        // ❌ Retain ciklus: ViewController → szolgáltatás → 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
    }
}

A Memory Graph-ban egy háromszöget fog látni: ViewController → DataService → closure → ViewController. A megoldás — a self gyenge megragadása: [weak self]. A javítás után a Memory Graph egy zöld élt mutat a closure-től a ViewController felé, és a retain cycle eltűnik.

swift
// Javított kód — a self gyenge megragadása
service.fetchData { [weak self] data in
    guard let self else { return }
    self.updateUI(data)
}

A Memory Graph Debugger felülete Xcode-ban

A Memory Graph Debugger felülete három panelből áll: bal — az összes élő objektum listája (osztályok szerint csoportosítva) a példányok számával; középső — vizuális gráf húzható csomópontokkal; jobb — a kiválasztott objektum vagy él inspektora. Az objektumlistában megjelenik: az osztály ikonja, a példányok száma a memóriában, a teljes retained size és a teljes heap százaléka. Az osztálynév szerinti szűrés támogatja a reguláris kifejezéseket.

Navigáció a gráfban

A gráf csomópontjai húzhatók az olvashatóság javítása érdekében. A csomópontra duplán kattintva részletes információ nyílik meg az objektumról: az összes tulajdonsága típusokkal és értékekkel, a hívási verem (backtrace) minden tulajdonsághoz és a retain/release előzmények. Backtrace — a kulcsfontosságú funkció: megmutatja, hogy melyik kódsor állította be pontosan a hivatkozást az objektumra. Ez lehetővé teszi a szivárgás forrásának megtalálását anélkül, hogy a teljes kódot kézzel kellene átnézni.

Összetett gráfokhoz az Xcode automatikus elrendezést biztosít a Layout → Hierarchical (hierarchikus) vagy Cluster (klaszter) segítségével. A hierarchikus elrendezés a gyökérobjektumokat felül, a gyermek objektumokat alul helyezi el, megkönnyítve a láncok keresését. A klaszter elrendezés a kapcsolódó objektumokat klaszterekbe csoportosítja, ami akkor kényelmes, ha a gráf több elkülönült csoportot tartalmaz. Az Apple szerint a legtöbb alkalmazáshoz a hierarchikus elrendezés ajánlott — intuitív és kevesebb időt vesz igénybe a vizuális elemzéshez.

lldb
// Az LLDB-parancsok, amelyeket a Memory Graph a motorháztető alatt használ
(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

Gráfelemzés: szivárgások keresése és megszüntetése

A Memory Graph elemzésének szisztematikus megközelítése több szakaszból áll. 1. szakasz: indítsa el az alkalmazást, hajtson végre egy olyan forgatókönyvet, amely potenciálisan szivárgást okoz (nyisson meg/zárjon be egy képernyőt, hajtson végre egy hálózati kérést). 2. szakasz: nyomja meg a Memory Graph gombot a Debug Navigatorban — az Xcode felépíti a gráfot. 3. szakasz: ellenőrizze a narancssárga retain cycle figyelmeztetéseket a bal panelen. 4. szakasz: gyanús objektumokhoz használja a Show only cycles opciót — csak a ciklikus hivatkozásokban részt vevő csomópontok jelennek meg.

A backtrace használata a forrás megtalálásához

Amikor a retain cycle megtalálásra került, kattintson a ciklus élére, és nyissa meg az inspektor panelt. A Backtrace szakaszban a hívási verem látható abban a pillanatban, amikor ez a hivatkozás be lett állítva. Például, ha az él a closure-től a self felé vezet, a backtrace megmutatja, hogy melyik metódusban és melyik kódsoron lett létrehozva a closure. Ez kiküszöböli a találgatás szükségességét — azonnal látja a problémás hivatkozás létrehozási pontját. A WWDC Labs szerint a backtrace elemzés 15–20 percről 2–3 percre csökkenti a retain cycle diagnosztizálási idejét.

swift
class ProfileViewController: UIViewController {
    var profileView: ProfileView!

    override func viewDidLoad() {
        super.viewDidLoad()
        profileView = ProfileView()
        // A Memory Graph itt mutatja a retain cycle-t
        profileView.onTap = { [unowned self] in
            // ⚠️ Az unowned crash-t okozhat nil self esetén
            self.navigateToDetail()
        }
    }

    func navigateToDetail() { }
}

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

A felesleges objektumok szűrése

A Memory Graph több ezer objektumot mutathat, megnehezítve a keresést. Használja a szűrőket a bal panelen: írja be az osztály nevét (pl. ProfileViewController), hogy csak annak az osztálynak a példányait jelenítse meg. Ezután válassza ki azt a példányt, amelyet fel kellett volna szabadítani (ha a képernyő be van zárva, de az objektum megmaradt). Alkalmazza a Show Reachable From funkciót — csak az ehhez az objektumhoz releváns hivatkozások jelennek meg, elrejtve a gráf többi részét.

Gyakorlati tippek a Memory Graph használatához

A tapasztalt fejlesztők a Memory Graph-ot nem csak a szivárgások keresésére használják, hanem a proaktív memóriakontrollra is. Ellenőrizze a Memory Graph-ot minden nagyobb architektúra-változtatás után — új delegate, closure vagy NotificationCenter feliratkozás hozzáadásakor. Elég egy tipikus forgatókönyvet végrehajtani, és meggyőződni arról, hogy az objektumok helyesen felszabadulnak, és retain cycles nem léteznek. Ez 2–3 percet vesz igénybe, de megelőz óráknyi későbbi hibakeresést.

Kombináció a Memory Reporttal

Memory Report az Xcode-ban (Debug Navigator fül) valós időben mutatja a memóriafogyasztás grafikonját. Használja együtt a Memory Graph-tal: nyissa meg a Memory Graph-ot hirtelen fogyasztásnövekedéskor. Például egy hosszú lista görgetésekor, ahol a cellák képeket töltenek be, a Memory Graph megmutatja, hogy mely objektumok jönnek létre és melyek szabadulnak fel. Ha az objektumok száma csökkenés nélkül nő — ez potenciális szivárgás, ami látható, mielőtt crash-hez vezetne. Az Apple szerint a Memory Graph + Memory Report kombináció az ajánlott munkafolyamat minden iOS-fejlesztő számára az Xcode 12-től kezdve.

objective-c
// Példa szivárgásra Objective-C-ben delegáláson keresztül
@interface DownloadManager : NSObject
@property (strong) id delegate; // ❌ Weak-nek kell lennie!
@end

@implementation DownloadManager
// A Memory Graph mutatja a retain cycle-t:
// ViewController → DownloadManager.delegate → ViewController
@end

// Javítás: weak property
@property (weak) id delegate;

A closure-ok profilozása

Különös figyelmet fordítson a closure-ökre — a retain cycles leggyakoribb forrására Swift-ben. Amikor a self megragadásra kerül egy olyan closure belsejében, amely az objektum tulajdonságaként van tárolva, klasszikus ciklus jön létre. A Memory Graph ezt egy closure-ként jeleníti meg ({} szimbólummal ellátott csomópont), kék élekkel kapcsolódva a megragadott objektumokhoz. Rendszeresen ellenőrizze az összes closure-t, különösen az aszinkron hívásokban, GCD-ben, Combine-ban és SwiftUI-ben használtakat. A Point-Free statisztikája szerint a Swift-projektekben a szivárgások 90%-a a self-et megragadó closure-ökhöz kapcsolódik.

Gyakran Ismételt Kérdések

A Memory Graph csak Objective-C-hez működik, vagy Swift-hez is?

A Memory Graph mindkét nyelvhez működik, mivel az Objective-C runtime-ot használja. Az ObjC-vel kompatibilis Swift-objektumok (NSObject leszármazottai, @objc-vel jelöltek) teljesen megjelennek. A tiszta Swift-struktúrák és ObjC-híd nélküli osztályok korlátozottan láthatók.

Miért nem mutat a Memory Graph bizonyos objektumokat?

Az objektumoknak regisztrálva kell lenniük az Objective-C runtime-ban. A Swift value types (struct, enum) nem jelennek meg. Győződjön meg róla, hogy az osztály az NSObject-ből származik, vagy használja a @objc attribútumot a Memory Graph-ban való láthatósághoz.

Hogyan kell értelmezni az élek színeit a gráfban?

Kék — strong reference, megtartja az objektumot. Zöld — weak reference, nem befolyásolja az életciklust. Szürke — unowned reference. Retain cycle csak kék élekből jön létre.

Lassítja-e a Memory Graph az alkalmazást?

A gráf felépítése 1–3 másodpercre felfüggeszti az alkalmazást, és ideiglenesen 200–500 MB-mal növelheti az Xcode memóriafogyasztását. Maga az alkalmazás nem lassul, mivel a vizsgálat a breakpoint szünet alatt történik.

Exportálható a Memory Graph elemzéshez?

Az Xcode nem támogatja a gráf közvetlen exportálását. Használjon képernyőképet a dokumentációhoz vagy lldb szkriptet heap.find_variable az adatok programozott kinyeréséhez. Részletes elemzéshez használja az Instruments Allocations-t heap dumppal.

Összefoglalás

  • Memory Graph — az Xcode vizuális eszköze a memóriában lévő objektumok gráfjának megjelenítéséhez a hivatkozásaikkal.
  • Retain cycle zárt kontúrként jelenik meg kék (strong) élekből — az Xcode narancssárgával emeli ki.
  • Backtrace a gráf minden éréhez pontosan megmutatja a kódban azt a helyet, ahol a problémás hivatkozás létrejött.
  • Szűrés osztályok és hivatkozástípusok szerint lehetővé teszi a szivárgások elkülönítését több ezer objektumot tartalmazó gráfban.
  • Closure-ök — a retain cycles fő forrása Swift-ben, a Memory Graph {} csomópontokként jeleníti meg őket.
  • Weak és unowned — megoldások a ciklus megszakítására, de a weak biztonságosabb nil esetén.
  • Rendszeres Memory Graph ellenőrzés architektúra-változtatások után megelőzi a memória-regressziót a projektben.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is