Quem trabalha com desenvolvimento para iOS provavelmente já se perguntou algo pelo menos uma vez.
“Por que o código antigo de Objective-C está cheio de retain e release?”
Se você começou com Swift, talvez tenha ficado confuso ao abrir código antigo.
Hoje vamos revisar a história do gerenciamento de memória do Objective-C, do MRC ao ARC.
Primeiro, o ponto principal.
O gerenciamento de memória do Objective-C passou da era em que os desenvolvedores contavam manualmente (MRC) para a era em que o compilador fazia isso por eles (ARC).
A coleta de lixo chegou a ser testada brevemente, mas o ARC apareceu em 2011 e acabou definindo o caminho.
Vamos examinar cada era.
O que é contagem de referências? Conceitos básicos de gerenciamento de memória
Antes de entrar na história, vamos fixar um conceito.
A contagem de referências (reference count).
Pense em cada objeto como tendo um número que conta quantas pessoas o estão mantendo.
Se o número for pelo menos 1, ele continua vivo; quando chega a 0, é liberado da memória.
Por isso, você aumenta o número ao manter um objeto (retain) e o diminui ao liberá-lo (release).
Essa regra simples é a base do gerenciamento de memória do Objective-C.
A era do MRC: quando os desenvolvedores contavam manualmente
Antes do ARC, na era do MRC (Manual Retain Count), esse número era gerenciado manualmente.
As regras eram claras. Muitos as memorizavam usando o termo “NARC”.
- Eu sou responsável pelos objetos criados com New, Alloc, Retain ou Copy
- Objetos pelos quais sou responsável devem executar
releasequando terminam de ser usados
O código real era assim.
// Ao criar um objeto, a contagem de referências fica 1
NSObject *obj = [[NSObject alloc] init];
[obj retain]; // contagem 2
[obj release]; // contagem 1
[obj release]; // contagem 0 → liberar memória
O problema começava aqui.
Se você esquecesse release, ocorria um vazamento de memória; se liberasse cedo demais, tocar em um objeto que já havia desaparecido encerrava o app.
O famoso crash de “objeto zumbi”.
Era preciso continuar contando mentalmente quantas referências o objeto mantido tinha, uma tarefa realmente cansativa.
autorelease: “Vou liberar um pouco mais tarde, não agora”
A era do MRC tinha mais uma situação complicada.
Isso acontecia quando um método criava um objeto novo e o retornava para fora.
- (NSString *)greeting {
NSString *msg = [[NSString alloc] initWithString:@"Olá"];
return msg; // Eu alloc criei, então devo release fazer isso, mas quando?
}
Pelas regras, quem chama alloc precisa executar release.
Mas, se você fizer release antes do return, o objeto desaparece antes de o receptor poder usá-lo. Se não fizer, haverá vazamento.
autorelease surgiu para resolver esse dilema.
Ele agenda o release para “não agora, mas daqui a pouco”.
Um objeto autoreleased entra em uma lista de espera chamada autorelease pool e recebe release quando o pool é esvaziado, normalmente ao fim de uma volta do run loop.
Assim, o receptor podia receber e usar o objeto com segurança.
Construtores de conveniência como [NSString stringWithFormat:], que retornavam objetos sem alloc, usavam todos esse método.
Esse autorelease pool sobreviveu na era do ARC com o nome de bloco @autoreleasepool. É uma ferramenta prática para picos de memória em loops, por isso tratei do assunto em outro artigo.
Por que a coleta de lixo falhou?
A Apple também conhecia esse inconveniente.
Por isso, em 2007, o Mac OS X 10.5 Leopard introduziu a coleta de lixo (GC), que limpava a memória automaticamente como o Java.
Parecia conveniente, pois os desenvolvedores não precisavam mais escrever retain nem release.
Mas os resultados não foram bons.
Como o GC rodava em segundo plano, o app podia travar brevemente em momentos imprevisíveis. Além disso, era pesado para o iPhone, sensível a desempenho e bateria.
Por isso, ele nunca foi introduzido no iOS.
Por fim, o GC para Mac foi descontinuado (deprecated) a partir do OS X 10.8, em 2012.
A chegada do ARC: o compilador conta por você
Então, em 2011, a solução apresentada pela Apple foi o ARC (Automatic Reference Counting).
Ele foi lançado com o Xcode 4.2, o iOS 5 e o OS X 10.7 Lion.
A ideia do ARC era inteligente.
A contagem de referências continuava igual, mas o compilador inseria
retainereleaseautomaticamente no lugar do desenvolvedor.
Diferentemente do GC, que executa uma limpeza separada em tempo de execução, o ARC insere o código de liberação nos pontos necessários durante a compilação.
Com isso, o gerenciamento de memória foi automatizado com quase nenhum impacto no desempenho em tempo de execução.
A comparação entre as duas eras fica assim.
| Categoria | MRC | ARC |
|---|---|---|
| Período de surgimento | Início–2011 | 2011 (iOS 5) |
| retain/release | Escrito manualmente | Inserido automaticamente pelo compilador |
| autorelease | Chamada direta | Gerenciado pelo compilador |
| Risco de vazamento de memória | Alto | Muito reduzido |
| Impacto no desempenho | Nenhum | Quase nenhum |
Mas o ARC também não é onipotente.
Como não consegue resolver referências cíclicas automaticamente, o desenvolvedor precisa quebrá-las com referências fracas como weak ou unowned.
Esse conceito continua exatamente igual no Swift que usamos hoje.
Perguntas frequentes (Q&A)
P. Ainda preciso aprender MRC?
Na prática, quase nunca é necessário escrever código novo com MRC. Ainda assim, bibliotecas antigas ou entrevistas podem cobrar o conceito, então vale conhecer os princípios.
P. autorelease também é usado com ARC?
Você não o chama diretamente, mas o mecanismo continua funcionando sob o ARC. Por isso, o bloco @autoreleasepool continua sendo útil.
P. O Swift também usa ARC?
Sim, o Swift também é baseado em ARC. Por isso, referências cíclicas e o conceito de weak são igualmente importantes no Swift.
Em resumo, o gerenciamento de memória do Objective-C evoluiu de “MRC contado à mão → breve experimento com GC → ARC contado pelo compilador”.
O gerenciamento automático de memória que hoje damos como certo foi aprimorado após inúmeros crashes e tentativas.
Espero que esse percurso seja um pequeno mapa para quem estuda desenvolvimento para iOS. Boa programação!

