Dispatch_once em Swift: por que isso é um erro e como escrever um singleton correto
\n\nSe você já se perguntou "Como fazer um singleton em Swift usando dispatch_once?" — você não está sozinho. No Stack Overflow, essa pergunta acumulou milhares de visualizações, e no código de muitos projetos ainda existem construções estranhas tentando emular a antiga abordagem do Objective-C. Mas a boa notícia é que em Swift existe uma solução muito mais elegante e segura. A má notícia: muitos desenvolvedores continuam cometendo os mesmos erros ao tentar transferir hábitos do Objective-C para o Swift.
\n\nNeste artigo, analisaremos os 5 principais erros ao usar dispatch_once em Swift, mostraremos como evitá-los e forneceremos um modelo pronto para um singleton correto. Vamos lá!
\n\nO que é dispatch_once e por que ele não é necessário em Swift?
\n\ndispatch_once é uma função do Grand Central Dispatch (GCD) que garante que um bloco de código seja executado exatamente uma vez durante toda a vida útil do aplicativo. Em Objective-C, essa era a maneira padrão de criar singletons thread-safe:
\n\n+ (instancetype)sharedInstance {\n static MyClass *sharedInstance = nil;\n static dispatch_once_t onceToken;\n dispatch_once(&onceToken, ^{\n sharedInstance = [[MyClass alloc] init];\n });\n return sharedInstance;\n}\n\nNo entanto, em Swift, a partir da versão 1.0, o dispatch_once não é suportado para uso em código do usuário. Sim, você pode importar o Dispatch e tentar chamá-lo, mas o compilador não permitirá. Por quê? Porque o Swift oferece recursos integrados que tornam o dispatch_once desnecessário.
Os 5 principais erros ao usar dispatch_once em Swift
\n\nErro 1: Tentar chamar dispatch_once diretamente
\n\nO erro mais comum é tentar escrever algo como:
\n\nclass MySingleton {\n static var shared: MySingleton = {\n let instance = MySingleton()\n // configuração\n return instance\n }()\n \n private init() {}\n}\n\nÀ primeira vista, isso funciona. Mas isso não é dispatch_once. É apenas uma variável global que é inicializada de forma lazy na primeira vez que é acessada. O Swift garante thread-safety para essas propriedades estáticas, mas não é a mesma coisa que dispatch_once.
\n\nPor que isso é ruim? Porque você perde o controle sobre o momento da inicialização. Se o seu singleton depende de recursos externos (como banco de dados ou rede), eles podem ainda não estar prontos.
\n\nErro 2: Usar dispatch_once através de um wrapper em Objective-C
\n\nAlguns desenvolvedores criam uma ponte em Objective-C para chamar dispatch_once a partir do Swift:
\n\n// Arquivo Objective-C\n@interface OnceHelper : NSObject\n+ (void)performOnce:(void(^)(void))block;\n@end\n\n@implementation OnceHelper\nstatic dispatch_once_t onceToken;\n+ (void)performOnce:(void(^)(void))block {\n dispatch_once(&onceToken, block);\n}\n@end\n\nDepois, em Swift:
\n\nclass MySingleton {\n static var shared: MySingleton = {\n var instance: MySingleton!\n OnceHelper.performOnce {\n instance = MySingleton()\n }\n return instance\n }()\n}\n\nQual é o problema? Primeiro, é uma gambiarra. Segundo, dispatch_once_t é uma variável global, e se você esquecer de redefinir o token (por exemplo, durante testes), o singleton não será recriado. Terceiro, a Apple recomenda explicitamente não usar dispatch_once em Swift.
\n\nErro 3: Esquecer a thread-safety
\n\nAlguns escrevem o singleton assim:
\n\nclass MySingleton {\n static var shared: MySingleton?\n \n private init() {}\n \n static func getInstance() -> MySingleton {\n if shared == nil {\n shared = MySingleton()\n }\n return shared!\n }\n}<