Dispatch_once en Swift: principales errores y alternativas correctas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Dispatch_once en Swift: por qué es un error y cómo escribir un singleton correcto

\n\n

Si alguna vez te has preguntado «¿Cómo hacer un singleton en Swift usando dispatch_once?» — no estás solo. En Stack Overflow, esta pregunta ha acumulado miles de visitas, y en el código de muchos proyectos aún se encuentran construcciones extrañas que intentan emular el antiguo enfoque de Objective-C. Pero la buena noticia es que en Swift hay una solución mucho más elegante y segura. La mala noticia: muchos desarrolladores siguen cometiendo los mismos errores al intentar trasladar hábitos de Objective-C a Swift.

\n\n

En este artículo analizaremos los 5 principales errores al usar dispatch_once en Swift, mostraremos cómo evitarlos y daremos una plantilla lista para un singleton correcto. ¡Vamos allá!

\n\n

¿Qué es dispatch_once y por qué no es necesario en Swift?

\n\n

dispatch_once es una función de Grand Central Dispatch (GCD) que garantiza que un bloque de código se ejecute exactamente una vez durante toda la vida de la aplicación. En Objective-C, era la forma estándar de crear singletons seguros para hilos:

\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\n

Sin embargo, en Swift, desde la versión 1.0, dispatch_once no es compatible para su uso en código de usuario. Sí, puedes importar Dispatch e intentar llamarlo, pero el compilador no lo permitirá. ¿Por qué? Porque Swift ofrece herramientas integradas que hacen que dispatch_once sea innecesario.

\n\n

Top 5 errores al usar dispatch_once en Swift

\n\n

Error 1: Intentar llamar a dispatch_once directamente

\n\n

El error más común es intentar escribir algo como:

\n\n
class MySingleton {\n    static var shared: MySingleton = {\n        let instance = MySingleton()\n        // configuración\n        return instance\n    }()\n    \n    private init() {}\n}
\n\n

A primera vista, esto funciona. Pero esto no es dispatch_once. Es simplemente una variable global que se inicializa de forma perezosa (lazy) al primer acceso. Swift garantiza la seguridad para hilos en estas propiedades estáticas, pero no es lo mismo que dispatch_once.

\n\n

¿Por qué es malo? Porque pierdes el control sobre el momento de la inicialización. Si tu singleton depende de recursos externos (por ejemplo, base de datos o red), es posible que aún no estén listos.

\n\n

Error 2: Usar dispatch_once a través de un envoltorio en Objective-C

\n\n

Algunos desarrolladores crean un puente en Objective-C para llamar a dispatch_once desde Swift:

\n\n
// Archivo 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\n

Luego en Swift:

\n\n
class MySingleton {\n    static var shared: MySingleton = {\n        var instance: MySingleton!\n        OnceHelper.performOnce {\n            instance = MySingleton()\n        }\n        return instance\n    }()\n}
\n\n

¿Cuál es el problema? Primero, es un parche. Segundo, dispatch_once_t es una variable global, y si olvidas restablecer el token (por ejemplo, durante pruebas), el singleton no se recreará. Tercero, Apple recomienda explícitamente no usar dispatch_once en Swift.

\n\n

Error 3: Olvidar la seguridad para hilos

\n\n

Algunos escriben el singleton así:

\n\n
class 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}                

Blogs

Book Recommendations