Ich versuche, ein geeignetes Singleton-Modell für die Verwendung in Swift zu erarbeiten. Bisher konnte ich ein nicht threadsicheres Modell wie folgt zum Laufen bringen:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
Das Umschließen der Singleton-Instanz in die statische Struktur sollte eine einzelne Instanz ermöglichen, die ohne komplexe Namensschemata nicht mit Singleton-Instanzen kollidiert, und die Dinge ziemlich privat machen. Offensichtlich ist dieses Modell jedoch nicht threadsicher. Also habe ich versucht dispatch_once
, das Ganze zu ergänzen :
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
static var token: dispatch_once_t = 0
}
dispatch_once(Static.token) { Static.instance = TPScopeManager() }
return Static.instance!
}
}
Aber ich bekomme einen Compilerfehler in der dispatch_once
Zeile:
Der Ausdruckstyp 'Void' kann nicht in den Typ '()' konvertiert werden.
Ich habe verschiedene Varianten der Syntax ausprobiert, aber alle scheinen die gleichen Ergebnisse zu erzielen:
dispatch_once(Static.token, { Static.instance = TPScopeManager() })
Was ist die richtige Verwendung von dispatch_once
Swift? Ich dachte anfangs, das Problem liege am Block aufgrund ()
der Fehlermeldung, aber je mehr ich es mir anschaue, desto mehr denke ich, dass es darum geht, die dispatch_once_t
richtige Definition zu erhalten.
@lazy
sollte jedoch jede Implementierung von thread-sicher sein.Static.instance = TPScopeManager()
erzwingt den Instanztyp. Wenn Sie so etwas wieStatic.instance = self()
einen erforderlichen Initialisierer verwenden, wird die entsprechende Typklasse generiert. Trotzdem, und das ist wichtig zu beachten, nur einmal für alle Instanzen in der Hierarchie! Der erste zu initialisierende Typ ist der für alle Instanzen festgelegte Typ. Ich glaube nicht, dass sich Objective-C gleich verhalten hat.Antworten:
tl; dr: Verwenden Sie den Klassenkonstantenansatz, wenn Sie Swift 1.2 oder höher verwenden, und den verschachtelten Strukturansatz , wenn Sie frühere Versionen unterstützen müssen.
Aus meiner Erfahrung mit Swift gibt es drei Ansätze zur Implementierung des Singleton-Musters, die eine verzögerte Initialisierung und Thread-Sicherheit unterstützen.
Klassenkonstante
Dieser Ansatz unterstützt die verzögerte Initialisierung, da Swift Klassenkonstanten (und Variablen) träge initialisiert und nach der Definition von threadsicher ist
let
. Dies ist jetzt offiziell empfohlen, um einen Singleton zu instanziieren.Klassenkonstanten wurden in Swift 1.2 eingeführt. Wenn Sie eine frühere Version von Swift unterstützen müssen, verwenden Sie den folgenden Ansatz für verschachtelte Strukturen oder eine globale Konstante.
Verschachtelte Struktur
Hier verwenden wir die statische Konstante einer verschachtelten Struktur als Klassenkonstante. Dies ist eine Problemumgehung für das Fehlen statischer Klassenkonstanten in Swift 1.1 und früheren Versionen und funktioniert weiterhin als Problemumgehung für das Fehlen statischer Konstanten und Variablen in Funktionen.
dispatch_once
Der traditionelle Objective-C-Ansatz wurde auf Swift portiert. Ich bin mir ziemlich sicher, dass es keinen Vorteil gegenüber dem verschachtelten Strukturansatz gibt, aber ich stelle ihn trotzdem hier, da ich die Unterschiede in der Syntax interessant finde.
In diesem GitHub- Projekt finden Sie Unit-Tests.
quelle
init
auch deklariert werden, umprivate
zu garantieren, dass während der gesamten Lebensdauer der App nur eine Instanz des Objekts vorhanden ist?final
damit Sie sie nicht unterordnen; und (b) Deklarieren derinit
Methode alsprivate
so, dass Sie nicht versehentlich irgendwo eine andere Instanz instanziieren können.Da Apple jetzt klargestellt hat, dass statische Strukturvariablen sowohl faul als auch eingewickelt initialisiert werden
dispatch_once
(siehe Hinweis am Ende des Beitrags), denke ich, dass meine endgültige Lösung sein wird:Dies nutzt die automatische verzögerte, thread-sichere Initialisierung statischer Strukturelemente, verbirgt die tatsächliche Implementierung sicher vor dem Verbraucher, hält alles kompakt für die Lesbarkeit unterteilt und eliminiert eine sichtbare globale Variable.
Apple hat klargestellt, dass Lazy Initialisierer threadsicher sind, sodass keine
dispatch_once
oder ähnliche Schutzmaßnahmen erforderlich sindVon hier aus
quelle
private init() {}
um die Tatsache weiter zu verstärken, dass diese Klasse nicht dazu gedacht ist, extern instanziiert zu werden.Für Swift 1.2 und höher:
Mit einem Korrektheitsnachweis (alle Gutschriften gehen hier ) gibt es jetzt kaum einen Grund, eine der vorherigen Methoden für Singletons zu verwenden.
Update : Dies ist jetzt die offizielle Methode, um Singletons zu definieren, wie in den offiziellen Dokumenten beschrieben !
Was Bedenken über die Verwendung
static
vsclass
.static
sollte auch dann verwendet werden, wennclass
Variablen verfügbar werden. Singletons sind nicht als Unterklassen gedacht, da dies zu mehreren Instanzen des Basis-Singletons führen würde. Die Verwendungstatic
erzwingt dies auf eine schöne, schnelle Art und Weise.Für Swift 1.0 und 1.1:
Mit den jüngsten Änderungen in Swift, den meist neuen Zugriffskontrollmethoden, neige ich jetzt zu einer saubereren Methode, eine globale Variable für Singletons zu verwenden.
Wie im Swift-Blog-Artikel hier erwähnt :
Diese Art, einen Singleton zu erstellen, ist threadsicher, schnell, faul und wird auch kostenlos mit ObjC verbunden.
quelle
private init() {}
als Initialisierer von hinzufügenSingletonClass
. um zu verhindern, dass von außen instanziiert wird.Swift 1.2 oder höher unterstützt jetzt statische Variablen / Konstanten in Klassen. Sie können also einfach eine statische Konstante verwenden:
quelle
Es gibt einen besseren Weg, dies zu tun. Sie können eine globale Variable in Ihrer Klasse über der Klassendeklaration wie folgt deklarieren:
Dies ruft nur Ihren Standard-Init auf oder welche Init- und globalen Variablen
dispatch_once
in Swift standardmäßig sind. Dann tun Sie in der Klasse, in der Sie eine Referenz erhalten möchten, einfach Folgendes:Grundsätzlich können Sie also den gesamten Block des gemeinsam genutzten Instanzcodes entfernen.
quelle
TPScopeManager.sharedInstance.doIt()
ständig schreiben , benennen Sie einfach Ihre KlasseTPScopeManagerClass
, haben Sie diese Deklaration neben der Klassepublic let TPScopeManager = TPScopeManagerClass()
und verwenden Sie einfach schreibenTPScopeManager.doIt()
. Sehr sauber!TPScopeManager
, und es handelt sich daher per Definition nicht um einen Singleton .Swift Singletons sind in dem Cocoa - Frameworks als Klassenfunktionen ausgesetzt, zum Beispiel
NSFileManager.defaultManager()
,NSNotificationCenter.defaultCenter()
. Daher ist es als Klassenfunktion sinnvoller, dieses Verhalten zu spiegeln, als als Klassenvariable wie bei einigen anderen Lösungen. z.B:Rufen Sie den Singleton über ab
MyClass.sharedInstance()
.quelle
static
Eigenschaften.Gemäß der Apple-Dokumentation wurde mehrfach wiederholt, dass der einfachste Weg, dies in Swift zu tun, eine statische Typ-Eigenschaft ist:
Wenn Sie jedoch nach einer Möglichkeit suchen, über einen einfachen Konstruktoraufruf hinaus zusätzliche Einstellungen vorzunehmen, besteht das Geheimnis darin, einen sofort aufgerufenen Abschluss zu verwenden:
Dies ist garantiert threadsicher und wird nur einmal träge initialisiert.
quelle
Swift 4+
quelle
Beim Betrachten des Beispielcodes von Apple bin ich auf dieses Muster gestoßen. Ich bin nicht sicher, wie Swift mit Statik umgeht, aber dies wäre in C # threadsicher. Ich füge sowohl die Eigenschaft als auch die Methode für Objective-C Interop hinzu.
quelle
dispatch_once
Sachen benutzen müssen . Ich wette auf deinen Stil. :)class
Entspricht eine Klassendeklaration nichtstatic
einer Strukturdeklaration?dispatch_once
Funktion profitieren .In Kürze,
Möglicherweise möchten Sie Dateien und Initialisierung lesen
quelle
Wenn Sie planen, Ihre Swift-Singleton-Klasse in Objective-C zu verwenden, generiert der Compiler bei diesem Setup die entsprechenden Objective-C-ähnlichen Header:
Dann können Sie in der Objective-C-Klasse Ihren Singleton so nennen, wie Sie es in Tagen vor Swift getan haben:
Dies ist nur meine einfache Implementierung.
quelle
NSFileManager.defaultManager()
, verwendet aber immer noch die faulen thread-sicheren statischen Member-Mechanismen von Swift.Erste Lösung
Später in Ihrem Code:
Zweite Lösung
Und später in Ihrem Code können Sie Klammern für weniger Verwirrung behalten:
quelle
Dann nenne es;
quelle
init
als zu markierenprivate
, sondern auch um dassharedMyModel
als zu machenfinal
! Für zukünftige Leser könnten wir in Swift 3 dazu neigen,sharedMyModel
einfach umzubenennenshared
.Verwenden:
Wie benutzt man:
quelle
Der beste Ansatz in Swift über 1.2 ist ein einzeiliger Singleton, da -
Um mehr über diesen Ansatz zu erfahren, können Sie diesen Link besuchen .
quelle
NSObject
Unterklasse? Abgesehen davon scheint dies im Wesentlichen dasselbe zu sein wie stackoverflow.com/a/28436202/1187415 .Aus Apple Docs (Swift 3.0.1),
quelle
Ich würde ein vorschlagen
enum
, wie Sie es in Java verwenden würden, zquelle
Nur als Referenz, hier ist ein Beispiel für eine Singleton-Implementierung der Nested Struct-Implementierung von Jack Wu / hpique. Die Implementierung zeigt auch, wie die Archivierung funktionieren könnte, sowie einige begleitende Funktionen. Ich konnte kein vollständiges Beispiel finden, also hilft das hoffentlich jemandem!
Und wenn Sie einige dieser Funktionen nicht erkannt haben, finden Sie hier eine kleine lebende Swift-Dienstprogrammdatei, die ich verwendet habe:
quelle
In Swift können Sie eine Singleton-Klasse folgendermaßen erstellen:
quelle
Ich bevorzuge diese Implementierung:
quelle
Meine Art der Implementierung in Swift ...
ConfigurationManager.swift
Greifen Sie von jedem Bildschirm der Anwendung aus wie folgt auf globalDic zu.
Lesen:
Schreiben:
quelle
Der einzig richtige Ansatz ist unten.
Zugreifen
Gründe dafür:
static
Die type-Eigenschaft wird garantiert nur einmal träge initialisiert, selbst wenn über mehrere Threads gleichzeitig zugegriffen wird, sodass keine Verwendung erforderlich istdispatch_once
init
Methode kann keine Instanz von anderen Klassen erstellt werden.final
Klasse, da Sie nicht möchten, dass andere Klassen die Singleton-Klasse erben.quelle
static let sharedInstance = Singleton()
Nachdem Sie Davids Implementierung gesehen haben, scheint es nicht erforderlich zu sein, eine Singleton-Klassenfunktion zu haben,
instanceMethod
dalet
dies fast dasselbe tut wie einesharedInstance
Klassenmethode. Alles was Sie tun müssen, ist es als globale Konstante zu deklarieren und das wäre es.quelle
quelle
dispatch_once
statischer Variablen verzögert ist und automatisch überdispatch_once
Apple geschützt wird. Aus diesem Grund wird empfohlen, anstelle von dispatch_once statische Daten zu verwenden .Singleton in der Vergangenheit schnell zu realisieren, ist nichts anderes als die drei Möglichkeiten: globale Variablen, interne Variablen und dispatch_once-Möglichkeiten.
Hier sind zwei gute Singleton. (Hinweis: Unabhängig von der Art des Schreibens muss die Privatisierungsmethode init () beachtet werden. In Swift ist der Konstruktorstandard des Objekts öffentlich, muss neu geschrieben werden. Init kann in privat umgewandelt werden Verhindern Sie standardmäßig andere Objekte dieser Klasse '()', um das Objekt zu erstellen.)
Methode 1:
Methode 2:
quelle
Dies ist die einfachste mit thread-sicheren Funktionen. Kein anderer Thread kann auf dasselbe Singleton-Objekt zugreifen, selbst wenn er dies wünscht. Swift 3/4
quelle
Ich verlangte von meinem Singleton, dass er die Vererbung zulässt, und keine dieser Lösungen erlaubte dies tatsächlich. Also habe ich mir Folgendes ausgedacht:
Singleton.sharedInstance()
ersten Ausführen die Instanz von zurückgegebenSingleton
SubSingleton.sharedInstance()
zuerst tun , wird die Instanz von zurückgegebenSubSingleton
.SubSingleton.sharedInstance()
istSingleton
es wahr und es wird dieselbe Instanz verwendet.Das Problem bei diesem ersten schmutzigen Ansatz ist, dass ich nicht garantieren kann, dass Unterklassen das implementieren
dispatch_once_t
und sicherstellen, dasssharedInstanceVar
es nur einmal pro Klasse geändert wird.Ich werde versuchen, dies weiter zu verfeinern, aber es wäre interessant zu sehen, ob jemand starke Gefühle dagegen hat (abgesehen von der Tatsache, dass es ausführlich ist und manuell aktualisiert werden muss).
quelle
Dies ist meine Implementierung. Außerdem wird verhindert, dass der Programmierer eine neue Instanz erstellt:
quelle
private init
wurde hier bereits vorgeschlagen: stackoverflow.com/a/28436202/1187415 .Ich benutze die folgende Syntax:
Dies funktioniert von Swift 1.2 bis 4 und hat mehrere Vorteile:
Singleton.instance
quelle