Memory Graph — het visuele hulpmiddel van Xcode Debug Navigator dat de graaf van objecten in het werkgeheugen van de applicatie weergeeft met hun onderlinge verwijzingen. In tegenstelling tot een heap dump, toont Memory Graph niet alleen een lijst van objecten, maar een gerichte graaf van verwijzingen, waarbij elk knooppunt een object is en elke rand een verwijzing (strong, weak, unowned). Volgens Apple WWDC 2018 stelt het hulpmiddel u in staat om retain cycles en geheugenlekken binnen enkele seconden visueel te detecteren, zonder dat u de ruwe heap dump-gegevens hoeft te analyseren.
Belangrijkste punten
Memory Graph — is een component van Xcode Debug Navigator (verschenen in Xcode 10, WWDC 2018) die een gerichte graaf bouwt van alle objecten in het geheugen van het te debuggen proces. Elk knooppunt van de graaf is een klasse-instantie (Objective-C of Swift), elke rand is een verwijzing naar een ander object. De kleur van de rand geeft het verwijzingstype aan: blauw — strong, groen — weak, grijs — unowned. De graaf wordt gebouwd op basis van LLDB- en Objective-C runtime-gegevens, daarom moet de applicatie voor een correcte werking zijn gecompileerd in de Debug-configuratie met ingeschakelde symbolen.
Werkingsprincipe: wanneer de applicatie is gestopt op een breakpoint, vraagt Xcode via LLDB aan de runtime alle levende objecten en hun verwijzingen op. LLDB gebruikt objc_getClassList en iteratie over allocatieregio's om de volledige graaf te bouwen. Op ARM64 (Apple Silicon) worden extra hardwaremiddelen gebruikt voor het volgen van allocaties zonder vertraging. De bouwtijd van de graaf hangt af van de heapgrootte: voor een typische iOS-applicatie (50–200 MB) wordt de graaf in 1–3 seconden gebouwd.
Volgens Apple is Memory Graph het enige hulpmiddel dat retain cycles kan visualiseren zonder codewijzigingen of het toevoegen van instrumentatie. In tegenstelling tot Instruments Leaks werkt Memory Graph in real-time binnen Xcode en vereist het geen aparte start van de profiler. Dit maakt het het eerste keuzehulpmiddel voor snelle diagnose van geheugenlekken tijdens het ontwikkelproces.
Heap dump geeft een tabel van alle objecten met getallen (shallow size, retained size) — het is optimaal voor kwantitatieve analyse. Memory Graph geeft een visueel beeld van verbindingen — het is optimaal voor het zoeken naar cyclische verwijzingen. De hulpmiddelen vullen elkaar aan: eerst Memory Graph voor snelle detectie van retain cycles, dan heap dump via Instruments Allocations voor nauwkeurige meting van retained size. Volgens de ervaring van objc.io dekt de combinatie van twee methoden 95% van de geheugenlekscenario's.
Retain cycle — een situatie waarin twee of meer objecten elkaar vasthouden met sterke verwijzingen, waardoor een gesloten contour ontstaat. ARC kan een dergelijke contour niet vrijgeven omdat de retain count van elk object nooit nul bereikt. Klassiek voorbeeld: ViewController en View, waarbij View een strong reference heeft naar een closure die self (ViewController) vastlegt. Memory Graph geeft dergelijke contouren weer als ringen (cycli) en markeert ze voor snelle identificatie.
Wanneer Xcode een retain cycle detecteert, markeert het deze met een oranje contour en toont een waarschuwing in Debug Navigator. Door op de cyclus te klikken, ziet u de keten van verwijzingen die de gesloten contour vormen. Het blijft aan de ontwikkelaar om te bepalen welke sterke rand zwak moet zijn — meestal is dit de verwijzing van het onderliggende object naar het bovenliggende object (bijv. delegate of closure).
class ViewController: UIViewController {
let service = DataService()
override func viewDidLoad() {
super.viewDidLoad()
// ❌ Retain cycle: ViewController → service → 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
}
}
In Memory Graph ziet u een driehoek: ViewController → DataService → closure → ViewController. De oplossing — zwak vastleggen van self: [weak self]. Na de correctie toont Memory Graph een groene rand van closure naar ViewController en verdwijnt de retain cycle.
// Gecorrigeerde code — zwak vastleggen van self
service.fetchData { [weak self] data in
guard let self else { return }
self.updateUI(data)
}
De interface van Memory Graph Debugger bestaat uit drie panelen: links — een lijst van alle levende objecten (gegroepeerd per klasse) met het aantal instanties; midden — een visuele graaf met versleepbare knooppunten; rechts — de inspector van het geselecteerde object of rand. In de objectlijst worden weergegeven: het klassypictogram, het aantal instanties in het geheugen, de totale retained size en het percentage van de volledige heap. Filteren op klassenaam ondersteunt reguliere expressies.
Knooppunten van de graaf kunnen worden versleept om de leesbaarheid te verbeteren. Dubbelklikken op een knooppunt opent gedetailleerde informatie over het object: alle eigenschappen met typen en waarden, de aanroepstack (backtrace) voor elke eigenschap en de retain/release-geschiedenis. Backtrace — de belangrijkste functie: het toont welke exacte coderegel de verwijzing naar het object heeft ingesteld. Dit maakt het mogelijk om de lekbron te vinden zonder de hele code handmatig door te nemen.
Voor complexe grafen biedt Xcode automatische lay-out via Layout → Hierarchical (hiërarchisch) of Cluster (cluster). Hiërarchische lay-out plaatst root-objecten bovenaan, onderliggende objecten onderaan, waardoor het zoeken naar ketens wordt vereenvoudigd. Clusterlay-out groepeert gerelateerde objecten in clusters, wat handig is wanneer de graaf verschillende geïsoleerde groepen bevat. Volgens Apple wordt voor de meeste applicaties hiërarchische lay-out aanbevolen — het is intuïtief en kost minder tijd voor visuele analyse.
// LLDB-commando's die Memory Graph onder de motorkap gebruikt
(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
Een systematische benadering van Memory Graph-analyse omvat verschillende fasen. Fase 1: start de applicatie, voer een scenario uit dat mogelijk een lek veroorzaakt (scherm openen/sluiten, een netwerkverzoek uitvoeren). Fase 2: druk op de Memory Graph-knop in Debug Navigator — Xcode bouwt de graaf. Fase 3: controleer de oranje retain cycle-waarschuwingen in het linkerpaneel. Fase 4: gebruik voor verdachte objecten de optie Show only cycles — alleen knooppunten die deel uitmaken van cyclische verwijzingen worden weergegeven.
Wanneer een retain cycle is gevonden, klikt u op de rand van de cyclus en opent u het inspectorpaneel. In de sectie Backtrace wordt de aanroepstack getoond op het moment dat deze verwijzing werd ingesteld. Als de rand bijvoorbeeld van closure naar self leidt, toont de backtrace in welke methode en op welke coderegel de closure is gemaakt. Dit elimineert de noodzaak om te raden — u ziet direct het creatiepunt van de problematische verwijzing. Volgens WWDC Labs verkort backtrace-analyse de diagnosetijd van een retain cycle van 15–20 minuten naar 2–3 minuten.
class ProfileViewController: UIViewController {
var profileView: ProfileView!
override func viewDidLoad() {
super.viewDidLoad()
profileView = ProfileView()
// Memory Graph zal retain cycle hier tonen
profileView.onTap = { [unowned self] in
// ⚠️ unowned kan crash veroorzaken bij nil self
self.navigateToDetail()
}
}
func navigateToDetail() { }
}
// ✅ Correct: [weak self] + guard let self
profileView.onTap = { [weak self] in
guard let self else { return }
self.navigateToDetail()
}
Memory Graph kan duizenden objecten tonen, wat het zoeken bemoeilijkt. Gebruik filters in het linkerpaneel: voer de klassenaam in (bijv. ProfileViewController) om alleen instanties van die klasse weer te geven. Selecteer vervolgens de instantie die had moeten worden vrijgegeven (als het scherm is gesloten maar het object is gebleven). Pas Show Reachable From toe — alleen verwijzingen die relevant zijn voor dit object worden getoond, de rest van de graaf wordt verborgen.
Ervaren ontwikkelaars gebruiken Memory Graph niet alleen voor het vinden van lekken, maar ook voor proactief geheugenbeheer. Controleer Memory Graph na elke grote architectuurwijziging — het toevoegen van een nieuwe delegate, closure of abonnement op NotificationCenter. Het is voldoende om een typisch scenario uit te voeren en ervoor te zorgen dat objecten correct worden vrijgegeven en retain cycles afwezig zijn. Dit kost 2–3 minuten, maar voorkomt uren aan later debuggen.
Memory Report in Xcode (tabblad Debug Navigator) toont een grafiek van het geheugengebruik in real-time. Gebruik het samen met Memory Graph: open Memory Graph bij een plotselinge toename van het gebruik. Bij het scrollen van een lange lijst met cellen die afbeeldingen laden, toont Memory Graph bijvoorbeeld welke objecten worden aangemaakt en welke worden vrijgegeven. Als het aantal objecten toeneemt zonder afname — is dit een potentieel lek, zichtbaar voordat het tot een crash leidt. Volgens Apple is de combinatie Memory Graph + Memory Report de aanbevolen workflow voor alle iOS-ontwikkelaars, te beginnen met Xcode 12.
// Voorbeeld van lek in Objective-C via delegatie
@interface DownloadManager : NSObject
@property (strong) id delegate; // ❌ Moet weak zijn!
@end
@implementation DownloadManager
// Memory Graph zal retain cycle tonen:
// ViewController → DownloadManager.delegate → ViewController
@end
// Correctie: weak property
@property (weak) id delegate;
Besteed speciale aandacht aan closures — de meest voorkomende bron van retain cycles in Swift. Bij het vastleggen van self binnen een closure die wordt opgeslagen als een eigenschap van het object, ontstaat een klassieke cyclus. Memory Graph geeft dit weer als een closure (knooppunt met het symbool {}), verbonden met blauwe randen naar de vastgelegde objecten. Controleer regelmatig alle closures, vooral die worden gebruikt in asynchrone aanroepen, GCD, Combine en SwiftUI. Volgens de statistieken van Point-Free is 90% van de lekken in Swift-projecten gerelateerd aan closures die self vastleggen.
Veelgestelde vragen
Memory Graph werkt voor beide talen, omdat het gebruikmaakt van Objective-C runtime. Swift-objecten die compatibel zijn met ObjC (overervend van NSObject, gemarkeerd met @objc) worden volledig weergegeven. Pure Swift-structuren en klassen zonder ObjC-brug zijn beperkt zichtbaar.
Objecten moeten geregistreerd zijn in de Objective-C runtime. Swift value types (struct, enum) worden niet weergegeven. Zorg ervoor dat de klasse overerft van NSObject of het kenmerk @objc gebruikt voor zichtbaarheid in Memory Graph.
Blauw — strong reference, houdt het object vast. Groen — weak reference, beïnvloedt de levenscyclus niet. Grijs — unowned reference. Een retain cycle wordt alleen gevormd uit blauwe randen.
Het bouwen van de graaf onderbreekt de applicatie gedurende 1–3 seconden en kan het geheugengebruik van Xcode tijdelijk met 200–500 MB verhogen. De applicatie zelf wordt niet trager, omdat de inspectie plaatsvindt tijdens de pauze op een breakpoint.
Xcode ondersteunt geen directe export van de graaf. Gebruik een screenshot voor documentatie of het lldb-script heap.find_variable voor programmatische gegevensextractie. Voor gedetailleerde analyse gebruikt u Instruments Allocations met een heap dump.
Samenvatting
{}-knooppunten.We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook