Memory Graph — визуелни алат Xcode Debug Navigator-а који приказује граф објеката у оперативној меморији апликације са њиховим међусобним референцама. За разлику од heap dump-а, Memory Graph не показује само листу објеката, већ усмерени граф референци, где је сваки чвор објекат, а свака ивица референца (strong, weak, unowned). Према подацима Apple WWDC 2018, алат омогућава визуелно откривање retain cycles-а и цурења меморије за неколико секунди, без потребе за анализом сирових података heap dump-а.
Главно
Memory Graph — то је компонента Xcode Debug Navigator-а (појавила се у Xcode 10, WWDC 2018) која гради усмерени граф свих објеката у меморији процеса који се дебагује. Сваки чвор графа је инстанца класе (Objective-C или Swift), свака ивица је референца ка другом објекту. Боја ивице указује на тип референце: плаве — strong, зелене — weak, сиве — unowned. Граф се гради на основу података LLDB-а и Objective-C runtime-а, због чега за исправан рад апликација мора бити компајлирана у Debug конфигурацији са укљученим симболима.
Принцип рада: када је апликација заустављена на breakpoint-у, Xcode преко LLDB-а захтева од runtime-а све живе објекте и њихове референце. LLDB користи objc_getClassList и итерацију кроз алокационе регионе за изградњу потпуног графа. На ARM64 (Apple Silicon) додатно се користе хардверска средства за праћење алокације без успоравања. Време изградње графа зависи од величине heap-а: за типичну iOS апликацију (50–200 MB) граф се гради за 1–3 секунде.
Према подацима Apple-а, Memory Graph је једини алат који може визуелизовати retain cycles без модификације кода или додавања инструментације. За разлику од Instruments Leaks-а, Memory Graph ради у реалном времену унутар Xcode-а и не захтева засебно покретање профилера. То га чини алатом првог избора за брзу дијагностику цурења меморије у процесу развоја.
Heap dump даје табелу свих објеката са бројевима (shallow size, retained size) — оптималан је за квантитативну анализу. Memory Graph даје визуелну слику веза — оптималан је за проналажење цикличних референци. Алати се допуњују: прво Memory Graph за брзо откривање retain cycles-а, затим heap dump преко Instruments Allocations-а за прецизно мерење retained size-а. Према искуству objc.io, комбинација две методе покрива 95% сценарија цурења меморије.
Retain cycle — ситуација када два или више објеката држе један другог јаким референцама, формирајући затворени контур. ARC не може ослободити такав контур, јер retain count сваког објекта никада не достиже нулу. Класичан пример: ViewController и View, где View има strong reference на closure који захвата self (ViewController). Memory Graph приказује такве контуре у облику прстенова (циклуса), истичући их за брзу идентификацију.
Када Xcode открије retain cycle, истиче га наранџастим контуром и приказује упозорење у Debug Navigator-у. Кликом на циклус видите ланац референци који формира затворени контур. Програмеру остаје да одреди која од јаких ивица треба да буде слаба — обично је то референца из подређеног објекта ка надређеном (нпр. delegate или closure).
class ViewController: UIViewController {
let service = DataService()
override func viewDidLoad() {
super.viewDidLoad()
// ❌ Retain cycle: ViewController → сервис → 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
}
}
У Memory Graph-у ћете видети троугао: ViewController → DataService → closure → ViewController. Решење — слабо захватање self-а: [weak self]. Након исправке, Memory Graph ће показати зелену ивицу од closure-а ка ViewController-у, а retain cycle ће нестати.
// Исправљен код — слабо захватање self-а
service.fetchData { [weak self] data in
guard let self else { return }
self.updateUI(data)
}
Интерфејс Memory Graph Debugger-а се састоји од три панела: леви — листа свих живих објеката (груписаних по класама) са бројем инстанци; централни — визуелни граф са превлачивим чворовима; десни — инспектор изабраног објекта или ивице. У листи објеката приказују се: икона класе, број инстанци у меморији, укупни retained size и проценат целог heap-а. Филтрирање по имену класе подржава регуларне изразе.
Чворови графа се могу превлачити ради побољшања читљивости. Двоструки клик на чвор отвара детаљне информације о објекту: сва његова својства са типовима и вредностима, стек позива (backtrace) за свако својство и историју retain/release. Backtrace — кључна функција: показује која тачно линија кода је поставила референцу на објекат. Ово омогућава проналажење извора цурења без ручног прегледања целог кода.
За сложене графове Xcode обезбеђује аутоматски распоред кроз Layout → Hierarchical (хијерархијски) или Cluster (кластерски). Хијерархијски распоред поставља коренске објекте на врх, подређене — на дно, поједностављујући проналажење ланаца. Кластерски груписање повезане објекте у кластере, што је згодно када граф садржи неколико изолованих група. Према подацима Apple-а, за већину апликација препоручује се хијерархијски распоред — интуитиван је и захтева мање времена за визуелну анализу.
// LLDB команде које Memory Graph користи испод хаубе
(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
Систематски приступ анализи Memory Graph-а укључује неколико фаза. Фаза 1: покрените апликацију, извршите сценарио који потенцијално изазива цурење (отворите/затворите екран, извршите мрежни захтев). Фаза 2: притисните дугме Memory Graph у Debug Navigator-у — Xcode ће изградити граф. Фаза 3: проверите наранџаста упозорења retain cycles-а у левом панелу. Фаза 4: за сумњиве објекте користите опцију Show only cycles — приказаће се само чворови који учествују у цикличним референцама.
Када је retain cycle пронађен, кликните на ивицу циклуса и отворите панел инспектора. У секцији Backtrace приказан је стек позива у тренутку када је ова референца постављена. На пример, ако ивица води од closure-а ка self-у, backtrace ће показати у ком методу и на којој линији кода је closure креиран. Ово елиминише потребу за погађањем — одмах видите тачку креирања проблематичне референце. Према подацима WWDC Labs, анализа backtrace-а смањује време дијагностике retain cycle-а са 15–20 минута на 2–3 минута.
class ProfileViewController: UIViewController {
var profileView: ProfileView!
override func viewDidLoad() {
super.viewDidLoad()
profileView = ProfileView()
// Memory Graph ће показати retain cycle овде
profileView.onTap = { [unowned self] in
// ⚠️ unowned може изазвати crash при nil self-у
self.navigateToDetail()
}
}
func navigateToDetail() { }
}
// ✅ Исправно: [weak self] + guard let self
profileView.onTap = { [weak self] in
guard let self else { return }
self.navigateToDetail()
}
Memory Graph може приказивати хиљаде објеката, отежавајући претрагу. Користите филтере у левом панелу: унесите име класе (нпр. ProfileViewController) да бисте приказали само инстанце те класе. Затим изаберите инстанцу која је требало да буде ослобођена (ако је екран затворен, а објекат је остао). Примените Show Reachable From — приказаће се само референце релевантне за овај објекат, скривајући остатак графа.
Искусни програмери користе Memory Graph не само за проналажење цурења, већ и за проактивну контролу меморије. Проверавајте Memory Graph након сваке веће промене архитектуре — додавања новог delegate-а, closure-а или претплате на NotificationCenter. Довољно је извршити типичан сценарио и уверити се да се објекти правилно ослобађају, а retain cycles не постоје. Ово траје 2–3 минута, али спречава сате каснијег дебаговања.
Memory Report у Xcode-у (картица Debug Navigator) приказује графикон потрошње меморије у реалном времену. Користите га заједно са Memory Graph-ом: отварајте Memory Graph при наглом порасту потрошње. На пример, при скроловању дугачке листе са ћелијама које учитавају слике, Memory Graph ће показати који објекти се креирају, а који ослобађају. Ако број објеката расте без смањења — то је потенцијално цурење, видљиво пре него што доведе до crash-а. Према подацима Apple-а, комбинација Memory Graph + Memory Report је препоручени workflow за све iOS програмере, почев од Xcode 12.
// Пример цурења у Objective-C-у преко делегације
@interface DownloadManager : NSObject
@property (strong) id delegate; // ❌ Треба да буде weak!
@end
@implementation DownloadManager
// Memory Graph ће показати retain cycle:
// ViewController → DownloadManager.delegate → ViewController
@end
// Исправка: weak property
@property (weak) id delegate;
Посебну пажњу посветите closure-има — најчешћем извору retain cycles-а у Swift-у. При захватању self-а унутар closure-а који се чува као својство објекта, формира се класичан циклус. Memory Graph то приказује као closure (чвор са симболом {}), повезан плавим ивицама са захваћеним објектима. Редовно проверавајте све closure-е, посебно оне који се користе у асинхроним позивима, GCD-у, Combine-у и SwiftUI-ју. Према статистици Point-Free, 90% цурења у Swift пројектима је повезано са closure-има који захватају self.
Често постављана питања
Memory Graph ради за оба језика, јер користи Objective-C runtime. Swift објекти компатибилни са ObjC-ом (наследници NSObject-а, означени са @objc) приказују се у потпуности. Чисте Swift структуре и класе без ObjC моста видљиве су ограничено.
Објекти морају бити регистровани у Objective-C runtime-у. Swift value types (struct, enum) се не приказују. Уверите се да класа наслеђује NSObject или користи атрибут @objc за видљивост у Memory Graph-у.
Плава — strong reference, држи објекат. Зелена — weak reference, не утиче на животни циклус. Сива — unowned reference. Retain cycle се формира само од плавих ивица.
Изградња графа зауставља апликацију на 1–3 секунде и може привремено повећати потрошњу меморије Xcode-а за 200–500 MB. Сама апликација не успорава, јер се инспекција одвија током паузе на breakpoint-у.
Xcode не подржава директан извоз графа. Користите снимак екрана за документацију или lldb скрипт heap.find_variable за програмско издвајање података. За детаљну анализу користите Instruments Allocations са heap dump-ом.
Закључци
{}.Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође