Strong Reference (referência forte) é um mecanismo padrão de gerenciamento de memória no qual um objeto permanece na memória enquanto pelo menos uma referência ativa apontar para ele. Ao contrário das referências fracas, uma referência forte incrementa o contador de referências do objeto e impede sua liberação automática. De acordo com a Apple Developer Documentation, o ARC gerencia automaticamente o ciclo de vida dos objetos em Swift e Objective-C. Compreender o funcionamento das referências fortes é fundamental para prevenir vazamentos de memória e dependências cíclicas em aplicações móveis.
Pontos principais
Strong Reference é um tipo de referência a um objeto que impede sua destruição pelo coletor de lixo ou sistema de gerenciamento de memória. Enquanto existir pelo menos uma referência forte ao objeto, sua memória não é liberada. Este é o mecanismo básico no qual se baseiam o ARC em Swift e Objective-C e a coleta de lixo em Java e Kotlin.
O conceito de referência forte é fundamental para todas as linguagens com gerenciamento automático de memória. Em sistemas com ARC, cada referência forte incrementa o contador de referências do objeto. Quando o contador chega a zero, o objeto é imediatamente desalocado. Em Java e Kotlin com coleta de lixo, uma referência forte garante que o objeto é alcançável e não será coletado pelo GC.
De acordo com a WWDC 2021, cerca de 35% dos vazamentos de memória em aplicações iOS estão relacionados ao uso incorreto de referências fortes e ciclos de retenção. No desenvolvimento Android, vazamentos por meio de referências fortes implícitas em closures e callbacks são a segunda causa mais frequente de problemas de memória depois do Context Leak.
Para trabalhar eficazmente com a memória, é necessário entender a diferença entre referências strong, weak e unowned, e escolher o tipo de referência adequado com base na propriedade e no ciclo de vida dos objetos.
Antes do ARC, os desenvolvedores chamavam manualmente retain e release para cada objeto, o que causava inúmeros erros. O ARC, apresentado pela Apple em 2011 com o LLVM 3.0, automatizou esse processo analisando o grafo de propriedade em tempo de compilação. O próprio compilador insere chamadas retain, release e autorelease onde necessário.
De acordo com o Clang Static Analyzer, a introdução do ARC reduziu os bugs relacionados à memória em aplicações iOS em 70%. Para os desenvolvedores, isso significa que o gerenciamento de memória se tornou mais seguro, mas ao mesmo tempo surgiu a necessidade de entender como as referências fortes funcionam internamente — para evitar retain cycles.
Em Kotlin e Java, o coletor de lixo desempenha o papel do ARC, mas o princípio da referência forte permanece o mesmo: GC Roots são os pontos de entrada pelos quais os objetos são mantidos por referências fortes. Enquanto um objeto for alcançável por uma cadeia de referências fortes a partir de uma GC Root, ele não será coletado.
ARC (Automatic Reference Counting) funciona contando as referências de cada objeto no heap. Quando uma nova referência forte a um objeto é criada, o contador aumenta (retain). Quando a referência é destruída ou sobrescrita, o contador diminui (release). Quando o contador chega a zero, o objeto é imediatamente removido da memória.
Considere um exemplo em Swift. Ao criar uma instância de uma classe, o ARC aloca memória e define o retain count como 1. Cada nova atribuição a outra variável incrementa o contador. Quando a variável sai do escopo, o contador diminui:
class ProfileViewController {
var nameLabel: String?
var avatarImage: UIImage?
func loadProfile() {
// retain count = 1 para nova instância
let user = User(name: "Ivan")
// retain count = 2 após atribuir nameLabel
nameLabel = user.name
// saída do método — user sai do escopo, retain count = 1
}
}
Neste código, o ARC garante que o objeto User permaneça na memória enquanto houver pelo menos uma referência forte apontando para ele. Quando a função loadProfile termina, a variável local user é destruída, mas nameLabel ainda mantém o objeto. A memória só será liberada quando nameLabel deixar de existir ou for sobrescrita.
Em Kotlin, um comportamento semelhante é fornecido por meio de GC Roots. Enquanto existir uma cadeia rastreável de referências fortes a partir de uma raiz do coletor de lixo (por exemplo, um campo estático ou uma thread ativa), o objeto permanece na memória. A diferença é que o GC não libera memória instantaneamente — isso ocorre de forma assíncrona após a análise de alcançabilidade.
No ARC, a liberação ocorre de forma síncrona quando o contador chega a zero. Em Swift e Objective-C, você sabe exatamente quando o objeto será removido. Em Kotlin e Java, o momento da liberação é imprevisível, mas isso é compensado por um esquema mais flexível para detectar dependências cíclicas no nível do coletor de lixo.
Retain cycle (ciclo de retenção) é uma situação em que dois ou mais objetos têm referências fortes mútuas entre si. Como resultado, seu retain count nunca chega a zero e a memória nunca é liberada, mesmo depois que os objetos não são mais necessários para a aplicação.
Um exemplo clássico: um view controller pai mantém um objeto filho com uma referência forte, e este por sua vez mantém o pai com uma referência forte. Isso é típico em situações com delegados, closures e expressões lambda aninhadas. De acordo com o Instruments Leaks, os retain cycles representam até 60% de todos os vazamentos de memória em aplicações que usam ARC.
class ParentViewController: UIViewController {
var child: ChildViewController?
func setupChild() {
child = ChildViewController()
// retain cycle: pai mantém filho, filho mantém pai via closure
child?.onEvent = {
self.handleEvent()
}
}
func handleEvent() {}
}
O problema aqui é que a closure onEvent captura self (ParentViewController) com uma referência forte, e o ParentViewController mantém child com uma referência forte. Ambos os objetos nunca serão liberados. A solução é usar weak self na closure para quebrar o ciclo.
Em Kotlin, ciclos semelhantes surgem quando lambdas capturam objetos externos. O coletor de lixo da JVM pode detectar esses ciclos com o tempo, mas apenas se os objetos forem inalcançáveis a partir de GC Roots. Se o ciclo estiver vinculado a uma thread ativa ou contexto de UI, o vazamento persiste por toda a vida da aplicação.
Entender a diferença entre os tipos de referência é fundamental para o gerenciamento seguro de memória. Strong Reference incrementa o retain count. Weak Reference não incrementa o retain count e se torna nil automaticamente quando o objeto é desalocado. Unowned Reference também não incrementa o retain count, mas não é zerada — acessá-la após a desalocação causa um crash.
| Tipo de referência | Retain count | Segurança | Quando usar |
|---|---|---|---|
| Strong | +1 | Segura (padrão) | Propriedade do objeto, relação pai → filho |
| Weak | Não altera | Anulação automática (segura) | Delegados, callbacks, referências inversas |
| Unowned | Não altera | Risco de crash ao acessar tardiamente | Quando o objeto vive garantidamente mais que o proprietário |
A escolha do tipo de referência é ditada pela relação de propriedade. Se o objeto B faz parte de A e não pode existir sem ele — use Strong. Se B pode existir independentemente e referencia A para notificações — use Weak. Unowned é raramente usado — apenas quando a vida do objeto filho estritamente não excede a vida do pai.
Apple Developer Documentation recomenda: por padrão, use strong para todas as relações de propriedade. Se precisar evitar um retain cycle — determine qual referência deve ser fraca. Geralmente é a referência inversa na hierarquia (filho → pai). Em Kotlin, um papel semelhante é desempenhado por WeakReference de java.lang.ref, usado para caches e padrões observer.
Detectar retain cycles é o primeiro passo. O segundo é eliminá-los corretamente. A ferramenta principal para quebrar ciclos de referências fortes é substituir uma das referências por weak ou unowned. Em linguagens com coleta de lixo, WeakReference com verificação manual de null antes de cada acesso é adicionalmente usado.
Em Swift e Objective-C, a correção mais comum é adicionar [weak self] nas closures. Isso garante que a closure não retenha o objeto após sua desalocação. Em Kotlin, wrappers WeakReference ou limpeza explícita de referências em onDestroy são usados para fins semelhantes.
class NetworkService {
func fetchData(completion: @escaping (Data?) -> Void) {
// captura com weak self — retain cycle eliminado
URLSession.shared.dataTask(
with: URL(string: "https://api.example.com")!
) { [weak self] data, response, error in
guard let self else { return }
completion(data)
}.resume()
}
}
Neste exemplo, [weak self] garante que NetworkService não seja retido pela closure depois que não for mais necessário. Se self for desalocado antes da requisição ser concluída — guard let self else { return } sai da closure sem chamar completion.
Para diagnóstico de retain cycles, use Instruments Leaks para iOS ou Android Profiler + LeakCanary para Android. Essas ferramentas mostram o grafo exato de retenção e indicam qual referência forte está impedindo a desalocação do objeto. A criação regular de perfis de memória deve fazer parte do pipeline CI/CD de qualquer projeto móvel.
Swift e Kotlin usam mecanismos de gerenciamento de memória fundamentalmente diferentes, mas o conceito de referência forte existe em ambos. Swift usa ARC com liberação síncrona ao atingir retain count = 0. Kotlin usa um GC de rastreamento que limpa assincronamente objetos inalcançáveis.
| Parâmetro | Swift (ARC) | Kotlin (JVM GC) |
|---|---|---|
| Mecanismo | Contagem de referências (retain count) | Rastreamento de alcançabilidade (GC Roots) |
| Liberação | Síncrona (quando o contador chega a zero) | Assíncrona (por ciclo do GC) |
| Retain cycle | Não detectado automaticamente | GC pode detectar, mas não imediatamente |
| Weak ref | weak (anulação automática) | WeakReference (verificação manual) |
A principal diferença prática: em Swift, um retain cycle é um vazamento garantido. Em Kotlin, o GC pode quebrar o ciclo se os objetos forem inalcançáveis da raiz, mas o tempo de vida dos objetos vazados permanece imprevisível. Portanto, em ambas as linguagens, a melhor estratégia é evitar ciclos de referências fortes na fase de projeto.
Para Swift, use weak em padrões de delegado e closures. Para Kotlin, use WeakReference ou componentes Lifecycle-aware que limpam automaticamente as referências quando o proprietário é destruído. Em ambas as abordagens, o objetivo é o mesmo — eliminar referências fortes onde elas criam uma cadeia de retenção inquebrável.
Perguntas frequentes
Strong Reference incrementa o retain count do objeto e impede sua liberação enquanto a referência existir. Weak Reference não altera o retain count e se torna nil automaticamente quando o objeto é removido da memória. Referências fortes são usadas para propriedade, fracas para conexões inversas e delegados.
Retain cycle é um bloqueio mútuo onde dois objetos se mantêm mutuamente com referências fortes. Seu retain count nunca chega a zero, a memória não é liberada. Isso leva a vazamentos de memória: os objetos permanecem no heap para sempre, a aplicação consome cada vez mais recursos e eventualmente trava com OutOfMemory.
Use Instruments Leaks do Xcode — execute a criação de perfil com o template Leaks, execute um cenário no aplicativo e verifique os indicadores de vazamento. Para diagnóstico preciso, mude para a guia Cycles & Roots — ela mostra o gráfico de referências fortes mútuas que formam um ciclo inquebrável.
Unowned deve ser usado quando a vida do objeto filho garantidamente não excede a vida do pai — por exemplo, ao vincular um objeto a um escopo estritamente definido. Em caso de dúvida, use Weak, pois acessar uma referência unowned liberada causa um crash da aplicação.
Indiretamente — sim. Cada retain e release no ARC é uma operação atômica com sobrecarga. Com um grande número de objetos em ciclos, isso pode afetar o desempenho. No entanto, o principal problema não é a velocidade do ARC, mas os vazamentos de memória devido a um tipo de referência mal escolhido.
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