Gerenciamento de memória em Swift: como funciona o ARC e o coletor de lixo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que o Swift não usa um coletor de lixo tradicional?

\n

Se você veio para o mundo Swift vindo do Python ou Java, provavelmente ficou surpreso que esta linguagem não possui um coletor de lixo (Garbage Collector) clássico. Em vez disso, a Apple implementou o mecanismo Automatic Reference Counting (ARC) — contagem automática de referências. E isso não é um capricho: em dispositivos móveis, cada ciclo de processador e cada byte de memória contam. O GC tradicional "congela" o aplicativo durante a coleta, o que para o iOS, com sua exigência de animações suaves (60 FPS), seria um desastre.

\n

Neste artigo, vamos analisar como o gerenciamento de memória realmente funciona em Swift, onde estão as armadilhas com referências cíclicas e como escrever código que não "vaza" para lugar nenhum.

\n\n

ARC: como funciona por baixo dos panos?

\n

ARC não é um coletor de lixo no sentido tradicional. Ele funciona na fase de compilação: o compilador insere instruções retain e release nos lugares certos. Mas há um detalhe: o ARC não sabe lidar com referências cíclicas (retain cycles). Se dois objetos se seguram com referências fortes, seus contadores de referência nunca cairão para zero — a memória vaza.

\n\n

Exemplo: classe simples e contagem de referências

\n
class Person {\n    let name: String\n    init(name: String) {\n        self.name = name\n        print("\\(name) apareceu na memória")\n    }\n    deinit {\n        print("\\(name) desapareceu da memória")\n    }\n}\n\nvar person1: Person? = Person(name: "Alice") // retain count = 1\nvar person2 = person1                        // retain count = 2\nperson1 = nil                                // retain count = 1\nperson2 = nil                                // retain count = 0 → deinit\n
\n

No console, você verá: "Alice apareceu na memória" e, após algum tempo, "Alice desapareceu da memória". Tudo correto.

\n\n

Referências cíclicas: o principal inimigo da memória

\n

Imagine: você tem um User e um Account. O usuário tem uma conta, e a conta conhece seu dono. Se ambas as propriedades forem referências fortes, forma-se um círculo vicioso:

\n\n
class User {\n    let name: String\n    var account: Account?\n    init(name: String) { self.name = name }\n    deinit { print("User \\(name) removido") }\n}\n\nclass Account {\n    let number: String\n    var owner: User?\n    init(number: String) { self.number = number }\n    deinit { print("Account \\(number) removido") }\n}\n\nvar user: User? = User(name: "Boris")\nvar account: Account? = Account(number: "12345")\nuser?.account = account\naccount?.owner = user\n\nuser = nil   // User não removido! account?.owner ainda mantém a referência\naccount = nil // Account não removido! user?.account mantém a referência\n// Vazamento de memória!\n
\n

Nenhuma mensagem deinit aparecerá no console. Os objetos ficam presos na memória para sempre. Isso é um retain cycle — um problema clássico de gerenciamento de memória em Swift.

\n\n

Como tratar? Weak e unowned

\n

A solução é tornar uma das referências fraca (weak) ou não proprietária (unowned). A diferença:

\n
    \n
  • weak — sempre opcional, torna-se automaticamente nil quando o objeto é removido. Seguro.
  • \n
  • unowned — não opcional, assume que o objeto vive mais tempo. Se acessar um objeto removido — crash.
  • \n
\n\n
class Account {\n    let number: String\n    weak var owner: User?  // ← referência fraca\n    init(number: String) { self.number = number }\n    deinit { print("Account \\(number) removido") }\n}\n// Agora user = nil liberará User, e account?.owner se tornará nil\n
\n\n

Coletor de lixo vs ARC: qual é mais rápido?

\n

Vamos ser honestos: ARC não é um "coletor de lixo" no sentido clássico. Um GC real (como em Java, Go ou Python) funciona em tempo de execução: ele periodicamente varre o heap, encontra objetos inacessíveis e os libera. Isso requer pausas (stop-the-world).

\n

ARC, por outro lado, é estático

Blogs

Book Recommendations