Ao criar uma tela que dispara muitos efeitos de jogo, chega um momento em que os frames começam a engasgar.
Se você continuar criando objetos de vida curta, como balas ou partículas, com init, verá no Instruments o gráfico de memória oscilar como uma serra.
É aí que entra o padrão Swift Object Pool.
Indo direto à conclusão: um object pool é uma técnica de reutilização que cria objetos antecipadamente, empresta-os e os devolve, em vez de criá-los toda vez. Seu objetivo é fundamentalmente diferente do Flyweight, que costuma causar confusão.
Neste artigo, vamos explicar o que é um object pool, como ele difere do Flyweight e como implementá-lo de verdade em Swift.
O que é o padrão Object Pool?
Um object pool cria antecipadamente vários objetos cujo custo de criação é alto e os armazena em um “pool”.
Em vez de criar um novo quando necessário, você retira um do pool e o devolve ao terminar.
O ponto central é a reutilização. O objetivo é reduzir o custo de alocação e liberação de memória causado pela repetição de init e deinit.
O resumo de um object pool em uma frase é: “Não crie; pegue emprestado e devolva.”
Ele se destaca principalmente nestas situações.
- Objetos como balas e partículas, criados e destruídos em grande quantidade em pouco tempo
- Recursos cuja criação é pesada, como conexões de rede e threads
- Views que são trocadas continuamente na tela, como a fila de reutilização de
UITableViewCell
Na verdade, se você desenvolve para iOS, provavelmente já usou um object pool. dequeueReusableCell é um object pool criado pela Apple.
Qual é a diferença para o Flyweight?
É fácil confundir os dois porque ambos passam a impressão de “economizar no uso de objetos”.
Mas os objetivos são completamente diferentes.
Um object pool é uma forma de reutilizar objetos como balas em momentos diferentes. Depois que você usa e devolve esta bala, a próxima assume o lugar dela.
Flyweight é uma forma de vários lugares referenciarem simultaneamente um único objeto compartilhado. Por exemplo, ao desenhar 10 mil árvores em uma floresta, você mantém uma única cópia de dados comuns, como cor e textura, e passa as posições separadamente.
A tabela resume a diferença desta forma.
| Categoria | Object Pool | Flyweight |
|---|---|---|
| Objetivo | Reduzir custos de criação e liberação | Reduzir o uso de memória |
| Forma de reutilização | Pegar emprestado e devolver (divisão temporal) | Compartilhar simultaneamente (divisão espacial) |
| Estado | Cada objeto mantém seu próprio estado | Separar estado compartilhado e estado externo |
| Exemplos típicos | Reutilização de células, partículas | Glifos de fontes, ícones de mapas |
Em uma linha: Object Pool é “reutilizar em turnos”, enquanto Flyweight é “compartilhar entre todos”.
Como criar um object pool em Swift
A estrutura é mais simples do que parece: basta um array para os objetos disponíveis e dois métodos para emprestá-los e devolvê-los.
Abaixo está um pool genérico simples.
final class ObjectPool<T> {
private var available: [T] = []
private let factory: () -> T
init(factory: @escaping () -> T) { self.factory = factory }
func acquire() -> T { available.popLast() ?? factory() } // Cria um novo se não houver
func release(_ item: T) { available.append(item) } // Devolve quando terminar
}
acquire() retira um objeto do pool quando há algum disponível e só cria um novo quando ele está vazio.
Ao devolvê-lo com release(), a próxima solicitação reutiliza esse objeto.
Quando aplicado a partículas, o fluxo fica assim.
let pool = ObjectPool<Particle> { Particle() }
let p = pool.acquire() // Pegar emprestado do pool
p.reset(at: point) // É importante inicializar o estado!
// ...Depois de terminar de usar na tela
pool.release(p) // Devolver
Há um ponto que você precisa lembrar: um objeto devolvido mantém o estado anterior.
Por isso, ao emprestá-lo novamente, é obrigatório passar por uma inicialização como reset() para evitar dados fantasmas.
Quando usar e quando evitar um object pool?
Usá-lo em qualquer lugar só porque é útil pode acabar sendo prejudicial.
Estes são os critérios que organizei depois de usá-lo na prática.
Recomendo nestes casos.
- Quando dezenas ou mais objetos são criados e destruídos repetidamente por segundo
- Quando o custo de criar um único objeto é claramente alto
- Quando o gráfico de memória oscila como uma serra e a sobrecarga de GC (coleta de lixo)/ARC (Automatic Reference Counting, contagem automática de referências) fica visível
Nestes casos, pense duas vezes.
- Se forem objetos leves, criados apenas uma ou duas vezes ocasionalmente, o custo de gerenciar o pool será maior
- Se você esquecer de devolver os objetos, o pool esvazia e você acaba criando um novo toda vez
- Em um ambiente multithread, o acesso ao pool exige um lock ou sincronização de fila
Swift tem muitos tipos por valor (struct) e o ARC é bastante eficiente, então não é necessário adotar um pool por padrão.
Meça primeiro com o Instruments e adote-o quando confirmar um gargalo.
Object Pool é um padrão de reutilização que economiza custos de criação, enquanto Flyweight é um padrão de compartilhamento que economiza memória.
Quando você entende bem a diferença, pode escolher a abordagem certa para cada situação.
Meça primeiro e adote-o exatamente quando necessário. Espero que você experimente o momento em que os frames ficam muito mais fluidos.

