Automatic Reference Counting (ARC) é um sistema de gerenciamento de memória em Swift e Objective-C que conta automaticamente o número de referências para cada objeto e o libera quando o contador chega a zero. De acordo com a Documentação Apple Swift, 2026, ARC é incorporado ao compilador e funciona em tempo de compilação, inserindo chamadas retain/release nos lugares certos. Ao contrário do Garbage Collection, ARC não requer uma thread separada de coletor e não cria pausas durante a execução da aplicação.
Pontos principais
ARC (Automatic Reference Counting) é um mecanismo de gerenciamento de memória baseado em compilador introduzido pela Apple no Xcode 4.2 (2011) para Objective-C e herdado pelo Swift. Ao contrário do gerenciamento manual de memória (Manual Retain-Release, MRR), o ARC automatiza completamente as chamadas retain, release e autorelease, inserindo-as em tempo de compilação sem intervenção do desenvolvedor.
ARC não é um coletor de lixo. É análise estática com inserção dinâmica de código: o compilador analisa os tempos de vida dos objetos e coloca retain/release nos pontos onde os objetos são criados, copiados ou saem de escopo. O resultado é liberação determinística de memória: o objeto é removido exatamente quando não há mais referências apontando para ele, sem atrasos ou pausas.
De acordo com a WWDC 2011 Session 323, a transição de MRR para ARC reduziu os bugs de crash relacionados à memória em 70% nas aplicações da Apple. Os desenvolvedores pararam de equilibrar manualmente retain/release, eliminando toda uma classe de vazamentos e erros de double-free.
Cada objeto na memória tem um contador de referências (retain count). Quando um objeto é criado, o contador é definido como 1. Quando uma nova referência strong aponta para o objeto — o contador aumenta (retain). Quando uma referência strong desaparece — o contador diminui (release). Ao chegar a zero, o objeto é imediatamente liberado.
O compilador Swift insere retain/release não em cada atribuição — ele usa análise estática para otimização. Por exemplo, se é garantido que um objeto não será usado após ser passado, o compilador pode pular um release/retain desnecessário. Esta otimização é chamada de ARC Optimization.
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) initialized (retain count: 1)")
}
deinit {
print("\(name) deallocated")
}
}
func testARC() {
let p = Person(name: "Alice") // retain count = 1
let q = p // retain count = 2
// q sai do escopo
// retain count = 1
// p sai do escopo
// retain count = 0 → deinit
}
Este exemplo mostra como o ARC gerencia o contador: ao atribuir q = p, o contador aumenta; quando q sai de escopo, diminui. Quando a última referência strong desaparece, o desinicializador é chamado imediatamente. Nenhum coletor de lixo espera — a memória é liberada na hora.
ARC e Garbage Collection resolvem o mesmo problema — gerenciamento automático de memória — mas com abordagens fundamentalmente diferentes. A escolha entre eles define a arquitetura da linguagem: Swift (ARC) vs Java/Go (GC). Vamos ver as principais diferenças.
| Característica | ARC (Swift/ObjC) | GC (Java/Go) |
|---|---|---|
| Momento da liberação | Determinístico: imediatamente quando o contador zera | Não determinístico: no próximo ciclo de coleta |
| Pausas de execução | Nenhuma (retain/release inseridos em compilação) | Pausas Stop-The-World (2–200 ms) |
| Sobrecarga | Incremento/decremento do contador a cada referência | Percorrimento do grafo de objetos, marcação, limpeza |
| Problemas | Retain Cycle (resolução manual) | Fragmentação do heap, vazamentos por referências esquecidas |
| Thread adicional | Não requerida | Requer thread do coletor de lixo |
A troca fundamental: ARC fornece tempos de vida previsíveis e zero pausas, mas exige que o desenvolvedor entenda retain cycles e escolha weak/unowned corretamente. GC libera o desenvolvedor dessas preocupações, mas ao custo de pausas não determinísticas e uma thread adicional.
ARC define três tipos de qualificadores de referência, cada um afetando o contador e o ciclo de vida do objeto de forma diferente. Escolher o qualificador correto é a base do gerenciamento seguro de memória em Swift.
Strong é o qualificador padrão. Cada referência strong aumenta o retain count do objeto em 1. Enquanto existir pelo menos uma referência strong, o objeto permanece vivo. Todas as propriedades de classe e variáveis locais em Swift são strong por padrão. Referências strong criam uma relação de posse: o objeto A possui o objeto B.
Weak é uma referência que não aumenta o retain count. Um objeto pode ser liberado mesmo que uma referência weak aponte para ele. Após a liberação, a referência weak é automaticamente definida como nil. Referências weak são sempre declaradas como var com tipo opcional (?). São usadas para quebrar retain cycles, especialmente no padrão delegate.
Unowned é uma referência não proprietária que, como weak, não aumenta o retain count. No entanto, uma referência unowned não é definida como nil após a liberação — acessar um objeto liberado causa um crash. Unowned é usado quando é garantido que o objeto vive pelo menos tanto quanto o objeto referenciador. Casos típicos são closures e relações pai-filho com tempo de vida garantido.
class Customer {
let name: String
var card: CreditCard? // strong
init(name: String) { self.name = name }
deinit { print("\(name) deallocated") }
}
class CreditCard {
let number: String
unowned let customer: Customer // unowned — não possui
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card \(number) deallocated") }
}
var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer e CreditCard ambos liberados — sem retain cycle
Aqui CreditCard usa uma referência unowned para Customer. Customer possui o cartão (strong), e o cartão não possui o cliente (unowned). Quando Customer é liberado, ambos os objetos são liberados — nenhum retain cycle ocorre. Se card.customer fosse strong, o ciclo bloquearia a liberação.
Apesar da automação, ARC não é uma panaceia. Os desenvolvedores encontram vários problemas típicos que exigem compreensão do mecanismo interno de gerenciamento de memória.
As closures em Swift capturam variáveis externas por referência strong. Se uma closure é atribuída a uma propriedade de classe e captura self — ocorre um retain cycle: a classe retém a closure, a closure retém self. A solução é uma lista de captura com weak ou unowned.
class NetworkManager {
var completionHandler: ((Data?) -> Void)?
var data: Data?
func fetchData() {
completionHandler = { [weak self] result in
guard let self else { return }
self.data = result
self.processResult()
}
}
func processResult() { }
}
A lista de captura [weak self] cria uma referência weak para self dentro da closure. Isso quebra o potencial retain cycle. Guard let self garante que o objeto está vivo antes de executar o código. weak self é a prática padrão para closures assíncronas em Swift.
Embora retain/release sejam operações leves, em loops intensivos os incrementos/decrementos frequentes do contador adicionam sobrecarga. Em Swift 5.9+, o compilador usa otimização que remove retain/release redundantes se o analisador provar que é seguro. No entanto, em Objective-C, retain/release ainda podem ser um gargalo em cenários de alta carga com milhões de chamadas por segundo.
Autorelease Pool é um mecanismo de liberação diferida usado em Objective-C e alguns cenários Swift. Os objetos são colocados no pool e recebem release quando o pool é drenado. Em loops com muitos objetos temporários (por exemplo, análise JSON), criar um autoreleasepool personalizado reduz o pico de consumo de memória.
Perguntas frequentes
No gerenciamento manual (MRR), o desenvolvedor chamava explicitamente retain, release e autorelease. ARC insere essas chamadas automaticamente em tempo de compilação, eliminando o risco de double-free, vazamentos por release esquecido e erros de equilíbrio retain/release.
ARC gerencia apenas objetos Objective-C e classes Swift. Para estruturas e ponteiros C/C++, ARC não se aplica — esses objetos são gerenciados manualmente ou via smart pointers C++ (shared_ptr, unique_ptr). Objetos Core Foundation (CFString, CGColor) também não estão cobertos pelo ARC.
weak — quando o objeto pode ser liberado antes do objeto referenciador (delegados, closures assíncronas). unowned — quando é garantido que o objeto vive pelo menos tanto quanto o referenciador (pai-filho onde o filho não pode existir sem o pai). Se não tiver certeza, escolha weak.
Tipos existenciais (protocol como tipo) em Swift encapsulam o valor em um contêiner especial (contêiner existencial). Isso aumenta o número de retain/release nos limites dos protocolos. Em Swift 5.7+, tipos de resultado opaco e parâmetros some reduzem a sobrecarga ao eliminar o contêiner.
Não há API direta para ler o retain count em Swift — é considerado um detalhe de implementação. Para diagnóstico, use Instruments (Allocations, Leaks) ou o Depurador de Memória no Xcode. Essas ferramentas mostram o número de instâncias de classe vivas e as cadeias de retenção.
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