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 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.
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 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).
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.
// 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 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.
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.
// 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
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.
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.
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 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.
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.
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.
// 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;
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 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.
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.
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.
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.
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
{} csomópontokként jeleníti meg őket.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.
Olvassa el is