Swift e Objective-C

@autoreleasepool: Guia completo (Como evitar picos de memória em loops)

“Na era do ARC, por que ainda usamos @autoreleasepool?”

3 min de leitura
Imagem de capa de @autoreleasepool: Guia completo (Como evitar picos de memória em loops)

“Na era do ARC, por que ainda usamos @autoreleasepool?”

É uma pergunta que recebo com frequência de desenvolvedores juniores. É uma palavra-chave que eles certamente já viram em algum lugar, mas explicar de imediato quando e por que usá-la é surpreendentemente difícil.

Hoje vou explicar o que é @autoreleasepool e quando usá-lo na prática.

Vamos começar pela conclusão.

@autoreleasepool é um bloco que libera, no momento que você escolher, objetos programados para serem liberados mais tarde. Seu principal uso é evitar picos de memória quando objetos temporários se acumulam dentro de um loop.

Vamos analisar isso passo a passo.


O que é um autorelease pool?

Vamos começar pelos conceitos básicos.

O Objective-C conta há muito tempo com autorelease. Ele permite programar um objeto para ser liberado “daqui a pouco, não agora”.

A lista de espera onde esses objetos programados ficam enfileirados é o autorelease pool.

Quando o pool é drenado, todos os objetos da lista recebem release de uma vez.

Então, quando esse pool é esvaziado?

Em apps iOS, o loop principal gerencia isso automaticamente. Ao final de cada ciclo — processar eventos de toque e atualizar a tela — ele esvazia completamente o pool.

Por isso, normalmente não precisamos nos preocupar.


O problema aparece nos loops

Porém, há situações em que a condição “ao final de cada ciclo” se torna um problema.

Isso acontece quando uma quantidade enorme de objetos temporários se acumula durante uma única iteração do loop principal.

Por exemplo, suponha que vamos processar milhares de fotos em um único loop.

for (int i = 0; i < 5000; i++) {
    // Um objeto de imagem grande é criado temporariamente a cada iteração
    UIImage *image = [self loadAndResizeImage:i];
    [self saveThumbnail:image];
}

Enquanto o loop está rodando, o loop principal não tem chance de esvaziar o pool.

Assim, objetos temporários apenas marcados para liberação se acumulam na memória — o equivalente a 5 mil fotos.

O gráfico de memória sobe como uma montanha e, em casos graves, o sistema encerra o app à força.

O momento em que o gráfico de memória sobe como uma montanha durante o loop
O momento em que o gráfico de memória sobe como uma montanha durante o loop

Como aplainar a montanha com @autoreleasepool

A solução é simples: crie um pool próprio dentro do loop e esvazie-o manualmente a cada iteração.

for (int i = 0; i < 5000; i++) {
    @autoreleasepool {
        UIImage *image = [self loadAndResizeImage:i];
        [self saveThumbnail:image];
    } // Os objetos temporários são liberados assim que o bloco termina
}

No momento em que o bloco @autoreleasepool é fechado, os objetos temporários criados dentro dele são liberados imediatamente.

Em vez de acumular memória equivalente a 5 mil fotos e liberá-la de uma vez, ela sobe e desce uma foto por vez.

O gráfico de memória, antes montanhoso, se transforma em um padrão suave de serra.


Como usar no Swift

Você pode pensar: “Eu só uso Swift”. O Swift também tem exatamente a mesma ferramenta.

É a função autoreleasepool.

for i in 0..<5000 {
    autoreleasepool {
        let image = loadAndResizeImage(i)
        saveThumbnail(image)
    }
}

Há um ponto importante a observar.

A maioria dos objetos Swift puros não passa por um autorelease pool; eles são liberados assim que o escopo termina.

Essa ferramenta realmente faz diferença ao chamar repetidamente APIs do Foundation e UIKit baseadas em Objective-C, como UIImage, Data(contentsOf:) e NSData.

Ainda existem muitas APIs que criam e retornam objetos autoreleased internamente.

Mesmo no Swift, um único bloco autoreleasepool pode suavizar o gráfico
Mesmo no Swift, um único bloco autoreleasepool pode suavizar o gráfico

Quando usar

Em resumo, @autoreleasepool é necessário em situações como estas:

  • Ao criar muitos objetos temporários grandes, como imagens, arquivos ou strings, dentro de um loop
  • Ao executar uma tarefa longa em uma thread em segundo plano sem um loop principal
  • Ao usar objetos Objective-C em um ambiente sem loop principal, como uma ferramenta de linha de comando

Por outro lado, normalmente não é necessário em código de UI comum ou lógica de negócio. O loop principal já gerencia isso corretamente.

Também não recomendo envolver código por hábito sem medir. Use-o depois de confirmar um pico real no Instruments ou no gráfico de memória do Xcode.


Perguntas frequentes (Q&A)

P. Se o ARC faz isso automaticamente, por que preciso gerenciar o pool?

O ARC (Automatic Reference Counting, contagem automática de referências) apenas insere chamadas retain e release por você; ele não muda quando o autorelease pool é esvaziado. Antecipar o momento em que o pool é esvaziado continua sendo responsabilidade do desenvolvedor.

P. O que é o @autoreleasepool na função main?

Abra o main.m em um projeto Objective-C e verá o app inteiro envolvido por @autoreleasepool. Esse é o pool do nível mais alto do app. O loop principal roda dentro dele e esvazia os pools filhos a cada ciclo.

P. Posso aninhar os blocos?

Sim. Os pools se acumulam como uma pilha. Quando o bloco interno é fechado, apenas o pool interno é esvaziado; o externo permanece intacto.

Os pools se acumulam como uma pilha e são esvaziados de dentro para fora
Os pools se acumulam como uma pilha e são esvaziados de dentro para fora

Em resumo, @autoreleasepool é um bloco que esvazia objetos temporários programados para liberação no momento que você escolher.

Se o Instruments mostrar a memória subindo como uma montanha em um loop, experimente envolver o interior desse loop com o bloco. Você verá o gráfico ficar mais estável.

Se quiser conhecer a história da migração de MRC (Manual Reference Counting, contagem manual de referências) para ARC, recomendo também ler o artigo anterior, “História do gerenciamento de memória no Objective-C”. Bom código!


Referências

Continue lendo