Singleton — o que é, uma única instância de classe no iOS e Android

Autor: IT Sectr Publicado: 2026-02-17 Tempo de leitura: 8 min

Singleton — um padrão de criação que garante uma única instância de classe e fornece um ponto de acesso global a ela. Singleton é amplamente usado no desenvolvimento móvel para recursos compartilhados: clientes de rede, bancos de dados, gerenciadores de configuração. O padrão é descrito no livro clássico GoF (1994) e continua sendo um dos mais reconhecíveis. Saiba mais em Refactoring Guru: Singleton.

Pontos principais

  • Singleton — garante uma instância de classe por aplicação
  • Ponto de acesso global — propriedade estática shared ou companion object
  • Thread safety — sincronização necessária para operação correta em ambientes multithread
  • Crítica — Singleton complica os testes e cria dependências ocultas
  • Alternativas — Dependency Injection, Service Locator para substituir Singleton

O que é Singleton: a essência do padrão singleton?

Singleton — um padrão de design de criação descrito pelo GoF (Gang of Four) em 1994. O padrão resolve dois problemas: restringe a instanciação de classe a um único objeto e fornece acesso global a esse objeto. Singleton é útil para recursos que devem ser únicos: fábricas de sessão, caches de imagem, gerenciadores de conexão de banco de dados, clientes Crashlytics ou Analytics.

Implementação do Singleton requer um construtor privado (impede criação externa), um campo estático com a única instância e um método de acesso estático (shared, instance, getInstance). Os clientes chamam Singleton.shared.method() sem se preocupar com a criação do objeto. O padrão é popular no iOS e Android: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — todos são Singletons. No entanto, o uso excessivo de Singleton leva ao antipadrão Global State.

Problemas do Singleton — dependências ocultas (as classes dependem implicitamente do objeto Singleton), complexidade de teste (não é possível substituir a instância em testes sem esforço adicional), violação do Princípio da Responsabilidade Única (Singleton gerencia tanto sua instância quanto a lógica de negócios). O desenvolvimento móvel moderno prefere DI (Dagger, Hilt, Swinject) para gerenciar instâncias únicas — o contêiner DI cria o objeto uma vez e o injeta através do construtor.

Singleton no iOS com Swift: shared e propriedades estáticas

Swift Singleton é implementado através de uma propriedade estática shared com um inicializador privado. Desde o Swift 3, a inicialização preguiçosa de propriedades estáticas é garantidamente thread-safe — o compilador adiciona automaticamente sincronização via dispatch_once. Basta declarar static let shared = Class() e tornar init() privado. Swift não requer sincronização adicional para acesso de thread única após a inicialização.

swift
final class NetworkManager {
    // Singleton seguro para threads
    static let shared = NetworkManager()

    private init() {
        URLSessionConfiguration.default.timeoutIntervalForRequest = 30
    }

    private var cache = NSCache<NSString, NSData>()

    func fetchData(from url: URL) async throws -> Data {
        let key = url.absoluteString as NSString
        if let cached = cache.object(forKey: key) {
            return cached as Data
        }
        let (data, _) = try await URLSession.shared.data(from: url)
        cache.setObject(data as NSData, forKey: key)
        return data
    }
}

// Uso
let data = try await NetworkManager.shared.fetchData(from: url)

Apple Singleton — muitos objetos do iOS SDK usam Singleton: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. A Apple usa Singleton para serviços fisicamente únicos (uma tela, uma aplicação). Os desenvolvedores copiam esse padrão para seus próprios serviços. No SwiftUI, o acesso global ao Singleton é substituído por Environment e @EnvironmentObject, melhorando a testabilidade.

Singleton no Android com Kotlin: companion object e object

Kotlin Singleton — a forma mais simples: a palavra-chave object declara uma classe singleton com inicialização preguiçosa no primeiro acesso. Kotlin object é thread-safe e não requer sincronização adicional. Se um Singleton com parâmetros de construtor for necessário, usa-se um companion object com um delegado lazy. No Android, Singleton é frequentemente necessário para o contexto Application e serviços que inicializam através de Application.onCreate().

kotlin
// Opção 1: object — Singleton simples sem parâmetros
object AppPreferences {
    private val prefs = Application.instance
        .getSharedPreferences("app", Context.MODE_PRIVATE)

    var isFirstLaunch: Boolean
        get() = prefs.getBoolean("first_launch", true)
        set(value) = prefs.edit { putBoolean("first_launch", value) }
}

// Opção 2: companion object — Singleton com parâmetros
class ApiClient private constructor(baseUrl: String) {
    companion object {
        @Volatile
        private var instance: ApiClient? = null

        fun getInstance(baseUrl: String): ApiClient {
            return instance ?: this.synchronized {
                instance ?: ApiClient(baseUrl).also { instance = it }
            }
        }
    }

    fun request(endpoint: String): String { /* ... */ }
}

Singleton do Android SDK — muitos serviços do sistema Android implementam Singleton: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Exemplos incluem SharedPreferences, MediaPlayer, AudioManager. Em aplicativos Android, Singleton é frequentemente usado para repositórios, gerenciadores e fábricas. O Google recomenda substituir Singleton por DI (Hilt, Koin), onde o escopo Singleton (Scope.Singleton ou @Singleton) é gerenciado pelo contêiner enquanto a classe permanece testável.

Thread safety: dispatch_once, synchronized e lock

Thread safety — um requisito crítico para Singleton em ambientes multithread. Sem sincronização, duas threads podem verificar simultaneamente instance == null e criar duas instâncias. A solução é bloquear durante a primeira criação e liberar após a inicialização. No Swift, propriedades estáticas (static let) são thread-safe por padrão. No Kotlin, object é thread-safe. Para estilo Java no Kotlin, usa-se synchronized ou @Volatile + double-check locking.

LinguagemMecanismoSegurança de threadInicialização preguiçosa
Swiftstatic letdispatch_once (automático)Sim, no primeiro acesso
Kotlin objectDeclaração de objectInicializador de classe thread-safeSim, no primeiro acesso
Kotlin companionsynchronized + @VolatileDouble-checked lockingSim, via lazy ou synchronized
Javasynchronized + volatileDouble-checked lockingSim, em getInstance()

Double-checked locking — um padrão para inicialização preguiçosa de Singleton. Primeira verificação sem sincronização (rápida se a instância já existe), segunda dentro de synchronized (criação por apenas uma thread). @Volatile garante visibilidade de mudanças para todas as threads. Sem volatile, outra thread pode ver um objeto parcialmente construído. No Kotlin, o delegado lazy com LazyThreadSafetyMode.SYNCHRONIZED implementa automaticamente double-checked locking.

Singleton vs Dependency Injection: quando usar

Dependency Injection — uma alternativa ao Singleton para gerenciar instâncias únicas. Um contêiner DI (Dagger, Hilt, Koin, Swinject) cria o objeto uma vez em um escopo Singleton e o injeta através do construtor. A classe não sabe sobre seu status Singleton — o contêiner decide. O código se torna testável: o módulo DI é substituído por um módulo mock nos testes. Vantagens do DI: dependências explícitas no construtor, capacidade de substituição, ciclo de vida unificado.

Quando Singleton é justificado — objetos de nível de sistema: Crashlytics, Analytics, Logging. Esses serviços são inicializados uma vez no AppDelegate/Application e usados em toda parte. DI é excessivo para eles. Singleton também é conveniente para caches de imagem (NSCache, Coil, Glide), onde o acesso global é justificado pelo desempenho. Para todo o resto, DI é preferível: torna as dependências visíveis, simplifica testes e refatoração.

Abordagem híbrida — Singleton com capacidade de substituição para testes. No Swift, um protocolo + propriedade estática que os testes podem substituir (por exemplo, via URLProtocol para URLSession). No Kotlin, uma classe aberta com uma propriedade injetável, onde os testes definem um mock através de reflexão ou um setter. Essa abordagem mantém a simplicidade do Singleton, mas fornece capacidades de teste. O Google recomenda Hilt para Android, a Apple não impõe DI para iOS — a escolha depende da equipe.

Perguntas frequentes

Singleton é um antipadrão?

Não, Singleton é um padrão GoF, mas seu uso incorreto frequente o transforma no antipadrão Global State. Singleton é justificado para recursos fisicamente únicos (tela, impressora, sistema de arquivos). Os problemas surgem quando Singleton é usado para gerenciamento de dados: dependências ocultas, complexidade de teste, violação do Princípio da Responsabilidade Única. Uma alternativa moderna é DI com escopo Singleton.

Como testar código que usa Singleton?

Três abordagens: (1) via protocolo — Singleton implementa um protocolo, os testes trocam a implementação; (2) via DI — Singleton é injetado como dependência através do construtor; (3) via método reset — Singleton tem um método para redefinir estado em testes (apenas para builds de teste). A primeira abordagem é preferível, a terceira é perigosa para produção. Swift permite substituir a propriedade shared através de manipulação em tempo de execução nos testes.

Como Kotlin object é diferente de Java Singleton?

Kotlin object é uma construção de linguagem que cria um Singleton no nível de bytecode. Diferente da implementação Java com construtor privado e getInstance(), object garante segurança de thread, inicialização preguiçosa e proíbe herança. Java Singleton requer sincronização manual (synchronized) e volatile para operação correta em ambientes multithread. Kotlin object é a forma mais segura e concisa no Android.

Singleton pode ser herdado?

Herdar Singleton quebra o padrão: se uma classe Singleton pode ser herdada, uma subclasse poderia criar uma segunda instância, violando a singularidade. No Swift, final class proíbe herança. Kotlin object não pode ser herdado (object é sealed). Se um Singleton com variabilidade for necessário, use um contêiner DI com escopo Singleton: ele garante uma única instância e suporta herança através de interfaces.

Como passar parâmetros para um Singleton no Android?

Os parâmetros são passados via init(context: Application) ou getInstance(param). Kotlin object não aceita parâmetros — use um companion object com um método de fábrica getInstance(param). Hilt resolve o problema: @Singleton + @Inject constructor(context: Application) — o contêiner DI injeta o contexto Application automaticamente. Para um cliente Retrofit, os parâmetros (baseUrl, interceptors) são passados através de um builder no módulo DI.

Resumo

  • Singleton — um padrão com instância única e acesso global
  • Swift shared — static let com segurança de thread garantida pelo compilador
  • Kotlin object — inicialização preguiçosa sem código adicional
  • Thread safety — double-checked locking para Java, automático para Swift/Kotlin
  • SDK da Apple — UIApplication.shared, UserDefaults.standard, FileManager.default
  • SDK do Android — Retrofit, Room, SharedPreferences via gerenciadores Singleton
  • Alternativas — Dependency Injection para código testável

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.

Discutir o projeto

Leia também