Dispatch_once in Swift: Warum das ein Fehler ist und wie man einen korrekten Singleton schreibt
\n\nWenn Sie sich jemals gefragt haben: "Wie erstelle ich einen Singleton in Swift mit dispatch_once?" – sind Sie nicht allein. Auf Stack Overflow hat diese Frage tausende Aufrufe gesammelt, und im Code vieler Projekte finden sich immer noch seltsame Konstruktionen, die versuchen, den alten Objective-C-Ansatz zu emulieren. Die gute Nachricht ist jedoch, dass es in Swift eine viel elegantere und sicherere Lösung gibt. Die schlechte Nachricht: Viele Entwickler machen immer wieder dieselben Fehler, wenn sie versuchen, Gewohnheiten aus Objective-C nach Swift zu übertragen.
\n\nIn diesem Artikel analysieren wir die Top-5-Fehler bei der Verwendung von dispatch_once in Swift, zeigen, wie man sie vermeidet, und geben eine fertige Vorlage für einen korrekten Singleton. Los geht's!
\n\nWas ist dispatch_once und warum wird es in Swift nicht benötigt?
\n\ndispatch_once ist eine Funktion aus Grand Central Dispatch (GCD), die garantiert, dass ein Codeblock während der gesamten Laufzeit der Anwendung genau einmal ausgeführt wird. In Objective-C war dies die Standardmethode zur Erstellung threadsicherer Singletons:
\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\nIn Swift ab Version 1.0 wird dispatch_once nicht unterstützt für die Verwendung in benutzerdefiniertem Code. Ja, Sie können Dispatch importieren und versuchen, es aufzurufen, aber der Compiler wird dies nicht zulassen. Warum? Weil Swift integrierte Mittel bietet, die dispatch_once überflüssig machen.
Top-5-Fehler bei der Verwendung von dispatch_once in Swift
\n\nFehler 1: Direkter Aufruf von dispatch_once
\n\nDer häufigste Fehler ist der Versuch, so etwas zu schreiben:
\n\nclass MySingleton {\n static var shared: MySingleton = {\n let instance = MySingleton()\n // Konfiguration\n return instance\n }()\n \n private init() {}\n}\n\nAuf den ersten Blick funktioniert das. Aber das ist kein dispatch_once. Es ist einfach eine globale Variable, die beim ersten Zugriff lazy initialisiert wird. Swift garantiert Threadsicherheit für solche statischen Eigenschaften, aber das ist nicht dasselbe wie dispatch_once.
\n\nWarum ist das schlecht? Weil Sie die Kontrolle über den Initialisierungszeitpunkt verlieren. Wenn Ihr Singleton von externen Ressourcen (z. B. Datenbank oder Netzwerk) abhängt, sind diese möglicherweise noch nicht bereit.
\n\nFehler 2: Verwendung von dispatch_once über einen Objective-C-Wrapper
\n\nEinige Entwickler erstellen eine Brücke zu Objective-C, um dispatch_once aus Swift aufzurufen:
\n\n// Objective-C Datei\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\nDann in 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\nWas ist das Problem? Erstens ist es ein Workaround. Zweitens ist dispatch_once_t eine globale Variable, und wenn Sie vergessen, das Token zurückzusetzen (z. B. beim Testen), wird der Singleton nicht neu erstellt. Drittens empfiehlt Apple ausdrücklich, dispatch_once in Swift nicht zu verwenden.
\n\nFehler 3: Threadsicherheit vergessen
\n\nEinige schreiben den Singleton so:
\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}<