Memory Graph é uma ferramenta visual do Xcode Debug Navigator que exibe um grafo dos objetos na memória do aplicativo com suas referências mútuas. Ao contrário de um heap dump, Memory Graph mostra não apenas uma lista de objetos, mas um grafo direcionado de referências onde cada nó é um objeto e cada aresta é uma referência (strong, weak, unowned). De acordo com Apple WWDC 2018, a ferramenta permite detectar visualmente retain cycles e vazamentos de memória em segundos, sem necessidade de analisar dados brutos do heap dump.
Principais pontos
Memory Graph é um componente do Xcode Debug Navigator (introduzido no Xcode 10, WWDC 2018) que constrói um grafo direcionado de todos os objetos na memória do processo depurado. Cada nó do grafo é uma instância de classe (Objective-C ou Swift), cada aresta é uma referência a outro objeto. A cor da aresta indica o tipo de referência: azul — strong, verde — weak, cinza — unowned. O grafo é construído com base em dados do LLDB e do Objective-C runtime, portanto o aplicativo deve ser compilado em configuração Debug com símbolos habilitados para funcionamento adequado.
Como funciona: quando o aplicativo está pausado em um breakpoint, o Xcode solicita ao runtime via LLDB todos os objetos vivos e suas referências. O LLDB usa objc_getClassList e itera sobre as regiões de alocação para construir o grafo completo. No ARM64 (Apple Silicon), meios de hardware adicionais são usados para rastreamento de alocação sem lentidão. O tempo de construção do grafo depende do tamanho do heap: para um aplicativo iOS típico (50–200 MB), o grafo é construído em 1–3 segundos.
De acordo com Apple, Memory Graph é a única ferramenta que pode visualizar retain cycles sem modificação de código ou adição de instrumentação. Ao contrário do Instruments Leaks, Memory Graph funciona em tempo real dentro do Xcode e não requer uma inicialização separada do profiler. Isso o torna a primeira ferramenta de escolha para diagnóstico rápido de vazamentos de memória durante o desenvolvimento.
Heap dump fornece uma tabela de todos os objetos com números (shallow size, retained size) — é ideal para análise quantitativa. Memory Graph fornece uma imagem visual das conexões — ideal para encontrar referências cíclicas. As ferramentas se complementam: primeiro Memory Graph para detecção rápida de retain cycles, depois heap dump via Instruments Allocations para medição precisa do retained size. De acordo com objc.io, a combinação de ambos os métodos cobre 95% dos cenários de vazamento de memória.
Retain cycle é uma situação em que dois ou mais objetos se seguram mutuamente com referências fortes, formando um loop fechado. O ARC não pode desalocar tal loop porque o retain count de cada objeto nunca chega a zero. Um exemplo clássico: ViewController e View, onde View tem uma referência forte a um closure que captura self (ViewController). Memory Graph exibe esses loops como anéis (ciclos), destacando-os para identificação rápida.
Quando o Xcode detecta um retain cycle, ele o destaca com um contorno laranja e mostra um aviso no Debug Navigator. Clicando no ciclo, você vê a cadeia de referências que formam o loop fechado. O desenvolvedor só precisa determinar qual aresta strong deve ser weak — geralmente é uma referência de um objeto filho para o pai (por exemplo, delegate ou 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
}
}
No Memory Graph você verá um triângulo: ViewController → DataService → closure → ViewController. A solução é tornar a captura de self fraca: [weak self]. Após a correção, o Memory Graph mostrará uma aresta verde do closure para o ViewController, e o retain cycle desaparecerá.
// Código corrigido — captura fraca de self
service.fetchData { [weak self] data in
guard let self else { return }
self.updateUI(data)
}
A interface do Memory Graph Debugger consiste em três painéis: o esquerdo — uma lista de todos os objetos vivos (agrupados por classe) com contagem de instâncias; o central — um grafo visual com nós arrastáveis; o direito — um inspetor do objeto ou aresta selecionado. A lista de objetos exibe: ícone da classe, número de instâncias na memória, retained size total e porcentagem de todo o heap. A filtragem por nome de classe suporta expressões regulares.
Os nós do grafo podem ser arrastados para melhorar a legibilidade. Um clique duplo em um nó abre informações detalhadas sobre o objeto: todas as suas propriedades com tipos e valores, a pilha de chamadas (backtrace) para cada propriedade e o histórico de retain/release. Backtrace é um recurso chave: mostra qual linha de código exata estabeleceu a referência ao objeto. Isso permite encontrar a origem do vazamento sem revisar manualmente todo o código.
Para grafos complexos, o Xcode fornece layout automático via Layout → Hierarchical ou Cluster. O layout hierárquico coloca objetos raiz no topo e filhos abaixo, simplificando a busca por cadeias. O agrupamento em cluster agrupa objetos relacionados, o que é conveniente quando o grafo contém vários grupos isolados. De acordo com Apple, para a maioria dos aplicativos, o layout hierárquico é recomendado — é intuitivo e leva menos tempo para análise visual.
// Comandos LLDB usados pelo Memory Graph internamente
(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
Uma abordagem sistemática para análise do Memory Graph inclui várias etapas. Etapa 1: execute o aplicativo, realize um cenário que potencialmente cause um vazamento (abrir/fechar uma tela, fazer uma requisição de rede). Etapa 2: clique no botão Memory Graph no Debug Navigator — o Xcode constrói o grafo. Etapa 3: verifique os avisos laranja de retain cycles no painel esquerdo. Etapa 4: para objetos suspeitos, use a opção Show only cycles — apenas os nós envolvidos em referências cíclicas serão exibidos.
Uma vez encontrado um retain cycle, clique na aresta do ciclo e abra o painel do inspetor. A seção Backtrace mostra a pilha de chamadas no momento em que essa referência foi estabelecida. Por exemplo, se a aresta leva de um closure ao self, o backtrace mostrará em qual método e em qual linha de código o closure foi criado. Isso elimina a necessidade de adivinhar — você vê imediatamente o ponto onde a referência problemática foi criada. De acordo com WWDC Labs, a análise de backtrace reduz o tempo de diagnóstico de retain cycle de 15–20 minutos para 2–3 minutos.
class ProfileViewController: UIViewController {
var profileView: ProfileView!
override func viewDidLoad() {
super.viewDidLoad()
profileView = ProfileView()
// Memory Graph mostrará o retain cycle aqui
profileView.onTap = { [unowned self] in
// ⚠️ unowned pode causar crash quando self é nil
self.navigateToDetail()
}
}
func navigateToDetail() { }
}
// ✅ Correto: [weak self] + guard let self
profileView.onTap = { [weak self] in
guard let self else { return }
self.navigateToDetail()
}
O Memory Graph pode exibir milhares de objetos, dificultando a busca. Use filtros no painel esquerdo: digite um nome de classe (por exemplo, ProfileViewController) para exibir apenas instâncias dessa classe. Em seguida, selecione uma instância que deveria ter sido desalocada (se a tela está fechada mas o objeto permanece). Aplique Show Reachable From — apenas as referências relevantes para este objeto serão exibidas, ocultando o resto do grafo.
Desenvolvedores experientes usam Memory Graph não apenas para encontrar vazamentos, mas também para controle proativo de memória. Verifique o Memory Graph após cada grande mudança arquitetural — adicionar um novo delegate, closure ou assinatura do NotificationCenter. Basta executar um cenário típico e garantir que os objetos estão sendo desalocados corretamente e que não há retain cycles. Isso leva 2–3 minutos, mas previne horas de depuração subsequente.
Memory Report no Xcode (aba Debug Navigator) mostra um gráfico de consumo de memória em tempo real. Use-o junto com Memory Graph: abra o Memory Graph quando houver um pico de consumo. Por exemplo, ao rolar uma lista longa com células carregando imagens, o Memory Graph mostrará quais objetos estão sendo criados e quais estão sendo desalocados. Se o número de objetos cresce sem diminuir — é um vazamento potencial visível antes de causar uma falha. De acordo com Apple, a combinação Memory Graph + Memory Report é o fluxo de trabalho recomendado para todos os desenvolvedores iOS a partir do Xcode 12.
// Exemplo de vazamento em Objective-C via delegation
@interface DownloadManager : NSObject
@property (strong) id delegate; // ❌ Deve ser weak!
@end
@implementation DownloadManager
// Memory Graph mostrará o retain cycle:
// ViewController → DownloadManager.delegate → ViewController
@end
// Correção: weak property
@property (weak) id delegate;
Preste atenção especial aos closures — a fonte mais comum de retain cycles em Swift. Ao capturar self dentro de um closure armazenado como propriedade de um objeto, forma-se um ciclo clássico. O Memory Graph exibe isso como um closure (um nó com o símbolo {}) conectado por arestas azuis aos objetos capturados. Verifique regularmente todos os closures, especialmente aqueles usados em chamadas assíncronas, GCD, Combine e SwiftUI. De acordo com estatísticas da Point-Free, 90% dos vazamentos em projetos Swift estão relacionados a closures que capturam self.
Perguntas frequentes
O Memory Graph funciona para ambas as linguagens porque usa o Objective-C runtime. Objetos Swift compatíveis com ObjC (subclasses de NSObject marcadas com @objc) são exibidos completamente. Estruturas e classes Swift puras sem ponte ObjC são vistas com limitações.
Os objetos devem estar registrados no Objective-C runtime. Swift value types (struct, enum) não são exibidos. Certifique-se de que a classe herde de NSObject ou use o atributo @objc para visibilidade no Memory Graph.
Azul — referência strong, retém o objeto. Verde — referência weak, não afeta o ciclo de vida. Cinza — referência unowned. Um retain cycle é formado apenas por arestas azuis.
A construção do grafo pausa o aplicativo por 1–3 segundos e pode aumentar temporariamente o consumo de memória do Xcode em 200–500 MB. O aplicativo em si não fica lento porque a inspeção ocorre durante a pausa do breakpoint.
O Xcode não suporta exportação direta do grafo. Use uma captura de tela para documentação ou o script lldb heap.find_variable para extração programática de dados. Para análise detalhada, use Instruments Allocations com um heap dump.
Resumo
{}.Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também