No capítulo sobre closures, [weak self] deixamos uma pergunta em aberto ao explicar como quebrar ciclos de referência. Também existe unowned: qual é a diferença e quando usar cada um? Para responder, primeiro precisamos entender como o ARC funciona por baixo.
Este é o primeiro artigo da série intermediária de Swift. Vamos organizar o funcionamento do ARC, as diferenças exatas entre as referências strong, weak e unowned e os critérios práticos para escolher entre elas. A história da migração de MRC (Manual Reference Counting, contagem manual de referências) do Objective-C para o ARC será tratada em outro artigo; aqui, o foco é o presente pela perspectiva do Swift.
A essência do ARC — não é um limpador de runtime, mas a contabilidade feita pelo compilador
Muita gente conhece o ARC (Automatic Reference Counting) como o “garbage collector do Swift”, mas seu funcionamento é fundamentalmente diferente.
A coleta de lixo (GC) executa periodicamente um sistema separado em runtime que procura e limpa objetos que “ninguém usa”. Já o ARC é decidido em tempo de compilação. O compilador analisa o fluxo do código e insere chamadas a retain (contador +1) onde uma referência surge e a release (contador -1) onde ela termina. Quando o contador chega a 0, deinit é chamado imediatamente e a memória é liberada.
Essa diferença produz duas propriedades importantes. Primeiro, a liberação é determinística. Não é “algum dia será limpo”, mas exatamente na linha em que a última referência desaparece. Essa é a base para delegar a liberação de recursos ao deinit. Segundo, não existe uma etapa de limpeza que pause o programa. É um exemplo concreto de “não abrir mão de desempenho em nome da segurança”, visto na primeira parte da série.
Não é gratuito. Os incrementos e decrementos do contador de referências precisam ser seguros entre threads, então são executados como operações atômicas; esse é o custo oculto dos tipos por referência. Esse é um dos motivos pelos quais o Swift favorece struct por padrão: tipos por valor não precisam dessa contabilidade. E, principalmente, o ARC não consegue quebrar ciclos. O GC encontra grupos de objetos inalcançáveis a partir das raízes e os limpa mesmo quando formam ciclos, mas no ARC o contador não chega a 0 enquanto eles continuarem contando uns aos outros. Por isso, os ciclos de referência continuam sendo responsabilidade do programador.
Três referências — a linguagem da posse
No mundo do ARC, as referências são divididas em três tipos conforme sejamos responsáveis por manter o objeto vivo.
strong (padrão) significa posse. Ele incrementa o contador, e o objeto permanece vivo enquanto o mantivermos. Toda referência declarada sem uma marca explícita é strong.
weak é uma observação sem posse. Como não incrementa o contador, o objeto é liberado quando todas as outras referências strong desaparecem. Nesse momento, o runtime transforma automaticamente a referência weak em nil. Por isso, uma variável weak precisa ser opcional e var. O fato de “poder se tornar nil a qualquer momento” fica registrado no tipo, como vimos no capítulo sobre opcionais.
unowned também é uma referência sem posse, mas abre mão do tratamento de nil. Assim como weak, não incrementa o contador, mas acessá-la depois que o objeto foi liberado causa uma falha imediata, em vez de nil. Em compensação, não é opcional e pode ser usada sem desembrulhar.
Em resumo: strong diz “vou manter você vivo”; weak, “sei que você pode desaparecer”; unowned, “tenho certeza de que você viverá mais do que eu”.
weak vs unowned — o critério é a relação de vida útil
Então, quando usar weak e quando usar unowned? O critério não é a sintaxe, mas a relação de vida útil entre os dois objetos.
Se o outro puder desaparecer antes de você, use weak. Delegate é o exemplo clássico. Quando uma view referencia seu delegate (geralmente um view controller), é perfeitamente normal que o view controller seja liberado primeiro. Por isso, a propriedade delegate costuma ser declarada weak var delegate e, ao usá-la, o optional chaining (delegate?.didFinish()) expressa “ignore se não existir”. É uma relação em que nil é um estado normal.
Se for estruturalmente garantido que o outro vive tanto quanto você ou mais, use unowned. O exemplo clássico é um cartão de crédito e seu cliente. O cartão não pode existir sem o cliente, e o cliente necessariamente está vivo enquanto o cartão existir. unowned let customer é a expressão exata. Como não é opcional, não há o ruído de desembrulhar a cada uso; além disso, pode ser declarado com let, preservando a imutabilidade.
Ao sobrepor esse critério ao de forced unwrapping !, a coerência fica clara. unowned é a versão de referência de !. É a declaração “se houver nil aqui, meu design foi quebrado”; quando essa certeza está errada, você recebe uma falha imediata em vez de um comportamento silenciosamente incorreto. Se não tiver certeza, use weak. Na prática, a regra dominante é “na dúvida, weak”: seu custo (tratamento de opcionais e uma pequena sobrecarga de runtime) é menor que o risco de uma falha. Pense em unowned como uma ferramenta para usar com parcimônia, apenas onde a garantia de vida útil esteja evidente na estrutura do código.
O mesmo critério se aplica à captura de closures. [weak self] é a escolha padrão porque, na maioria dos casos, não há garantia de que self estará vivo quando o closure for executado. Por outro lado, unowned é justificável quando as vidas do closure e de self estão vinculadas, como ao capturar self em um closure de execução imediata de uma propriedade lazy. Se a propriedade está viva, self também está.
Verificando com ferramentas — instrumentação, não intuição
Ciclos de referência não são todos encontrados em revisão de código. Conhecer três ferramentas transforma o “provavelmente está tudo bem” em “eu verifiquei”.
Logs de deinit são a ferramenta mais barata. Se você fecha a tela e o deinit do view model não aparece, há um vazamento em algum lugar. Criar o hábito de adicionar um print às classes suspeitas durante o desenvolvimento permite encontrar cedo a maioria dos ciclos.
O Memory Graph Debugger do Xcode mostra os ciclos visualmente. Ao clicar no botão do gráfico de memória na barra de depuração durante a execução, os objetos do heap atual e suas relações de referência aparecem em um gráfico. Se objetos que deveriam ter sido liberados permanecerem, é possível rastrear pelas setas quem os está retendo. Objetos suspeitos de vazamento aparecem com um ponto de exclamação roxo, o que também é uma pista.
O template Leaks do Instruments faz instrumentação ao longo do tempo. Ele é adequado para verificações periódicas antes do lançamento: abra e feche a tela repetidamente e observe se a memória cresce em degraus.
Por sinal, os suspeitos habituais dos ciclos são conhecidos: closures armazenados em propriedades (capítulo sobre closures), declarar o delegate como strong por engano e esquecer de cancelar registros no NotificationCenter ou em timers (capítulo sobre NSTimer). Ao revisar código novo, verificar esses três pontos já cobre a maioria dos ciclos.
Resumo
- O ARC não é um limpador de runtime: ele insere retain/release em tempo de compilação. A liberação é determinística e não há pausas do GC, mas ele não consegue quebrar ciclos sozinho.
- Referências são a linguagem da posse. strong possui; weak não possui e admite nil; unowned não possui e presume que o outro viverá mais.
- O critério é a relação de vida útil: weak se o outro puder desaparecer primeiro; unowned se estruturalmente viver mais; weak em caso de dúvida.
- unowned é o forced unwrapping das referências. Use-o apenas onde a certeza estiver evidente no código.
- Verifique com instrumentação, não intuição: logs de deinit, Memory Graph Debugger e Instruments Leaks.
O próximo artigo é o segundo da série intermediária: genéricos. Vamos organizar como o T entre os sinais de menor e maior combina segurança de tipos e reutilização de código, além de quando uma cláusula where é necessária.
Artigos recomendados
- [Swift intermediário #2] Do básico ao uso dos genéricos (Generics) do Swift: como
elimina duplicação e riscos ao mesmo tempo - [Swift intermediário #3] Swift some vs any: explicação completa, a identidade de some View e o custo de existential
- [Swift intermediário #4] Domine os tipos associados (associatedtype) do Swift

![Imagem de capa de [Swift intermediário #1] ARC: escolha weak ou unowned pela vida útil](/assets/images/posts/dea74de0-7d82-4c90-b8e1-e7be2bdaf005/1.jpg)