Memory Graph ist ein visuelles Tool des Xcode Debug Navigators, das einen Graphen der Objekte im Arbeitsspeicher der Anwendung mit ihren gegenseitigen Referenzen anzeigt. Im Gegensatz zu einem Heap-Dump zeigt Memory Graph nicht nur eine Liste von Objekten, sondern einen gerichteten Referenzgraphen, bei dem jeder Knoten ein Objekt und jede Kante eine Referenz (strong, weak, unowned) ist. Laut Apple WWDC 2018 ermöglicht das Tool, Retain Cycles und Speicherlecks in Sekunden visuell zu erkennen, ohne Rohdaten eines Heap-Dumps analysieren zu müssen.
Wichtige Punkte
Memory Graph ist eine Komponente des Xcode Debug Navigators (eingeführt in Xcode 10, WWDC 2018), die einen gerichteten Graphen aller Objekte im Speicher des debugged Prozesses erstellt. Jeder Knoten des Graphen ist eine Klasseninstanz (Objective-C oder Swift), jede Kante eine Referenz auf ein anderes Objekt. Die Farbe der Kante gibt den Referenztyp an: blau — strong, grün — weak, grau — unowned. Der Graph wird auf Basis von LLDB- und Objective-C-Runtime-Daten erstellt, daher muss die Anwendung in der Debug-Konfiguration mit aktivierten Symbolen kompiliert werden, um ordnungsgemäß zu funktionieren.
Funktionsweise: Wenn die Anwendung an einem Breakpoint angehalten wird, fordert Xcode über LLDB alle lebenden Objekte und deren Referenzen von der Runtime an. LLDB verwendet objc_getClassList und iteriert über Speicherbereiche, um den vollständigen Graphen zu erstellen. Auf ARM64 (Apple Silicon) werden zusätzliche Hardware-Mechanismen zur Verfolgung von Speicherzuweisungen ohne Verlangsamung verwendet. Die Erstellungszeit des Graphen hängt von der Heap-Größe ab: Für eine typische iOS-Anwendung (50–200 MB) wird der Graph in 1–3 Sekunden erstellt.
Laut Apple ist Memory Graph das einzige Tool, das Retain Cycles ohne Codeänderung oder zusätzliche Instrumentierung visualisieren kann. Im Gegensatz zu Instruments Leaks arbeitet Memory Graph in Echtzeit innerhalb von Xcode und erfordert keinen separaten Profiler-Start. Dies macht es zum ersten Mittel der Wahl für die schnelle Diagnose von Speicherlecks während der Entwicklung.
Heap-Dump liefert eine Tabelle aller Objekte mit Zahlen (Shallow Size, Retained Size) — optimal für die quantitative Analyse. Memory Graph liefert ein visuelles Bild der Verbindungen — optimal zum Auffinden zyklischer Referenzen. Die Tools ergänzen sich: Zuerst Memory Graph zur schnellen Erkennung von Retain Cycles, dann Heap-Dump über Instruments Allocations zur genauen Messung der Retained Size. Laut objc.io deckt die Kombination beider Methoden 95% der Speicherleckszenarien ab.
Retain Cycle ist eine Situation, in der zwei oder mehr Objekte sich gegenseitig mit starken Referenzen halten und einen geschlossenen Kreislauf bilden. ARC kann einen solchen Kreislauf nicht freigeben, da der Retain Count jedes Objekts niemals Null erreicht. Ein klassisches Beispiel: ViewController und View, wobei View eine starke Referenz auf einen Closure hat, der self (ViewController) erfasst. Memory Graph zeigt solche Kreisläufe als Ringe (Zyklen) an und hebt sie zur schnellen Identifizierung hervor.
Wenn Xcode einen Retain Cycle erkennt, hebt er ihn mit einer orangefarbenen Umrandung hervor und zeigt eine Warnung im Debug Navigator an. Ein Klick auf den Zyklus zeigt die Kette von Referenzen, die den geschlossenen Kreislauf bilden. Der Entwickler muss nur bestimmen, welche starke Kante schwach sein sollte — normalerweise ist dies eine Referenz von einem untergeordneten Objekt zum übergeordneten (z. B. delegate oder 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 sehen Sie ein Dreieck: ViewController → DataService → closure → ViewController. Die Lösung besteht darin, die Erfassung von self schwach zu machen: [weak self]. Nach der Korrektur zeigt Memory Graph eine grüne Kante vom Closure zum ViewController, und der Retain Cycle verschwindet.
// Korrigierter Code — schwache Erfassung von self
service.fetchData { [weak self] data in
guard let self else { return }
self.updateUI(data)
}
Die Oberfläche des Memory Graph Debuggers besteht aus drei Bereichen: links — eine Liste aller lebenden Objekte (nach Klassen gruppiert) mit Instanzanzahl; Mitte — ein visueller Graph mit verschiebbaren Knoten; rechts — ein Inspektor für das ausgewählte Objekt oder die Kante. Die Objektliste zeigt: Klassensymbol, Anzahl der Instanzen im Speicher, gesamte Retained Size und den Prozentsatz des gesamten Heaps. Die Filterung nach Klassennamen unterstützt reguläre Ausdrücke.
Die Knoten des Graphen können zur besseren Lesbarkeit verschoben werden. Ein Doppelklick auf einen Knoten öffnet detaillierte Informationen zum Objekt: alle Eigenschaften mit Typen und Werten, den Aufrufstapel (Backtrace) für jede Eigenschaft und den Retain/Release-Verlauf. Backtrace ist eine Schlüsselfunktion: Sie zeigt, welche genaue Codezeile die Referenz zum Objekt erstellt hat. Dies ermöglicht das Auffinden der Leckquelle, ohne den gesamten Code manuell durchsehen zu müssen.
Für komplexe Graphen bietet Xcode automatisches Layout über Layout → Hierarchical oder Cluster. Das hierarchische Layout platziert Wurzelobjekte oben und Kindobjekte darunter, was die Kettenuche vereinfacht. Die Cluster-Gruppierung fasst verwandte Objekte in Clustern zusammen, was nützlich ist, wenn der Graph mehrere isolierte Gruppen enthält. Laut Apple wird für die meisten Anwendungen das hierarchische Layout empfohlen — es ist intuitiv und benötigt weniger Zeit für die visuelle Analyse.
// LLDB-Befehle, die Memory Graph intern verwendet
(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
Ein systematischer Ansatz zur Memory Graph-Analyse umfasst mehrere Schritte. Schritt 1: Führen Sie die Anwendung aus, führen Sie ein Szenario durch, das möglicherweise ein Leck verursacht (Bildschirm öffnen/schließen, Netzwerkanfrage stellen). Schritt 2: Klicken Sie auf die Memory Graph-Schaltfläche im Debug Navigator — Xcode erstellt den Graphen. Schritt 3: Prüfen Sie die orangefarbenen Retain-Cycle-Warnungen im linken Bereich. Schritt 4: Verwenden Sie für verdächtige Objekte die Option Show only cycles — es werden nur Knoten angezeigt, die an zyklischen Referenzen beteiligt sind.
Sobald ein Retain Cycle gefunden wurde, klicken Sie auf die Kante des Zyklus und öffnen Sie den Inspektorbereich. Der Abschnitt Backtrace zeigt den Aufrufstapel zum Zeitpunkt der Erstellung dieser Referenz. Wenn die Kante beispielsweise von einem Closure zu self führt, zeigt der Backtrace, in welcher Methode und in welcher Codezeile das Closure erstellt wurde. Dies macht Raten überflüssig — Sie sehen sofort den Punkt, an dem die problematische Referenz erstellt wurde. Laut WWDC Labs reduziert die Backtrace-Analyse die Diagnosezeit für Retain Cycles von 15–20 Minuten auf 2–3 Minuten.
class ProfileViewController: UIViewController {
var profileView: ProfileView!
override func viewDidLoad() {
super.viewDidLoad()
profileView = ProfileView()
// Memory Graph zeigt hier den Retain Cycle an
profileView.onTap = { [unowned self] in
// ⚠️ unowned kann bei nil self einen Crash verursachen
self.navigateToDetail()
}
}
func navigateToDetail() { }
}
// ✅ Richtig: [weak self] + guard let self
profileView.onTap = { [weak self] in
guard let self else { return }
self.navigateToDetail()
}
Memory Graph kann Tausende von Objekten anzeigen, was die Suche erschwert. Verwenden Sie Filter im linken Bereich: Geben Sie einen Klassennamen ein (z. B. ProfileViewController), um nur Instanzen dieser Klasse anzuzeigen. Wählen Sie dann eine Instanz aus, die hätte freigegeben werden sollen (wenn der Bildschirm geschlossen ist, das Objekt aber noch vorhanden ist). Wenden Sie Show Reachable From an — es werden nur für dieses Objekt relevante Referenzen angezeigt, der Rest des Graphen wird ausgeblendet.
Erfahrene Entwickler verwenden Memory Graph nicht nur zum Auffinden von Lecks, sondern auch zur proaktiven Speicherkontrolle. Überprüfen Sie Memory Graph nach jeder größeren Architekturänderung — Hinzufügen eines neuen Delegaten, Closures oder NotificationCenter-Abonnements. Führen Sie einfach ein typisches Szenario aus und stellen Sie sicher, dass Objekte korrekt freigegeben werden und keine Retain Cycles vorhanden sind. Dies dauert 2–3 Minuten, verhindert aber stundenlanges späteres Debugging.
Memory Report in Xcode (Registerkarte Debug Navigator) zeigt ein Echtzeit-Diagramm des Speicherverbrauchs. Verwenden Sie es zusammen mit Memory Graph: Öffnen Sie Memory Graph bei einem starken Anstieg des Verbrauchs. Beim Scrollen einer langen Liste mit Zellen, die Bilder laden, zeigt Memory Graph beispielsweise, welche Objekte erstellt und welche freigegeben werden. Wenn die Anzahl der Objekte ohne Abnahme wächst — handelt es sich um ein potenzielles Leck, das sichtbar ist, bevor es zu einem Absturz führt. Laut Apple ist die Kombination Memory Graph + Memory Report der empfohlene Workflow für alle iOS-Entwickler ab Xcode 12.
// Beispiel für ein Leck in Objective-C über Delegation
@interface DownloadManager : NSObject
@property (strong) id delegate; // ❌ Muss weak sein!
@end
@implementation DownloadManager
// Memory Graph zeigt den Retain Cycle an:
// ViewController → DownloadManager.delegate → ViewController
@end
// Behebung: weak property
@property (weak) id delegate;
Achten Sie besonders auf Closures — die häufigste Ursache für Retain Cycles in Swift. Beim Erfassen von self innerhalb eines Closures, das als Eigenschaft eines Objekts gespeichert wird, entsteht ein klassischer Zyklus. Memory Graph zeigt dies als Closure (einen Knoten mit dem Symbol {}), der durch blaue Kanten mit den erfassten Objekten verbunden ist. Überprüfen Sie regelmäßig alle Closures, insbesondere solche, die in asynchronen Aufrufen, GCD, Combine und SwiftUI verwendet werden. Laut Point-Free-Statistiken stehen 90% der Lecks in Swift-Projekten im Zusammenhang mit Closures, die self erfassen.
Häufig gestellte Fragen
Memory Graph funktioniert für beide Sprachen, da es die Objective-C-Runtime verwendet. Mit ObjC kompatible Swift-Objekte (mit @objc markierte NSObject-Unterklassen) werden vollständig angezeigt. Reine Swift-Strukturen und -Klassen ohne ObjC-Brücke werden eingeschränkt angezeigt.
Objekte müssen in der Objective-C-Runtime registriert sein. Swift-Wertetypen (struct, enum) werden nicht angezeigt. Stellen Sie sicher, dass die Klasse von NSObject erbt oder das @objc-Attribut für die Sichtbarkeit in Memory Graph verwendet.
Blau — starke Referenz, hält das Objekt fest. Grün — schwache Referenz, beeinflusst den Lebenszyklus nicht. Grau — unowned-Referenz. Ein Retain Cycle wird nur aus blauen Kanten gebildet.
Die Erstellung des Graphen pausiert die Anwendung für 1–3 Sekunden und kann den Xcode-Speicherverbrauch vorübergehend um 200–500 MB erhöhen. Die Anwendung selbst wird nicht verlangsamt, da die Inspektion während der Breakpoint-Pause erfolgt.
Xcode unterstützt keinen direkten Export des Graphen. Verwenden Sie einen Screenshot für die Dokumentation oder das lldb-Skript heap.find_variable zur programmatischen Datenextraktion. Für detaillierte Analysen verwenden Sie Instruments Allocations mit einem Heap-Dump.
Zusammenfassung
{}-Knoten an.Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch