Mit einem dispatch_once singleton-Modell in Swift
Ich bin versuchen zu arbeiten, eine entsprechende singleton-Modell für den Einsatz im Swift. Bisher habe ich in der Lage zu bekommen, eine non-thread-safe-Modell arbeiten:
class var sharedInstance:TPScopeManager {
get {
struct Static {
static var instance : TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
Verpackung die singleton-Instanz in der Statischen Struktur sollte es eine einzige Instanz, die nicht kollidieren mit singleton-Instanzen ohne komplexe Benennung schemings, und es sollten die Dinge ziemlich privat. Offensichtlich aber dieses Modell ist nicht thread-sicher, so habe ich versucht, hinzuzufügen dispatch_once zu der ganzen Sache:
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 bekomme ich einen compiler-Fehler auf der dispatch_once
Linie:
Nicht konvertieren kann der Ausdruck den Typ 'Void' zu geben '()'
Ich habe versucht, verschiedene Varianten der syntax, aber Sie alle scheinen die gleichen Ergebnisse:
dispatch_once(Static.token, { Static.instance = TPScopeManager() })
Was ist die richtige Verwendung von dispatch_once
mit Swift? Anfänglich dachte ich, das problem mit dem block durch die ()
in der Fehlermeldung, aber je mehr ich schaue, desto mehr denke ich, dass es möglicherweise eine Frage der dispatch_once_t
korrekt definiert ist.
Das ist es, was ich meinte. Leider haben wir immer noch nicht genug Informationen über die Interna. Jedoch, IMHO die Implementierung von
@lazy
sollte thread-sicher.Und dieser Weg hat auch den Vorteil, dass keine Freilegung der Umsetzung der predations der Anrufer.
Es scheint auch gar nicht möchte, kann @lazy class-Variablen.
Seien Sie vorsichtig! Zwei Dinge zu beachten, die mit diesem Ansatz. Zuerst werden alle Klassen, die Erben, diese überschreiben die sharedInstance Eigenschaft.
Static.instance = TPScopeManager()
Kräfte des instance-Typs. Wenn Sie so etwas wie Static.instance = self()
mit einer notwendigen Initialisierung der entsprechenden Typ-Klasse generiert werden. Auch so, und das ist die wichtige Sache zu beachten, nur einmal für alle Instanzen in der Hierarchie! Ersten Typs zu initialisieren, ist der Typ set für alle Instanzen. Ich glaube nicht, dass objective-c verhielten sich gleich.InformationsquelleAutor David Berry | 2014-06-03
Du musst angemeldet sein, um einen Kommentar abzugeben.
tl;dr: Verwenden Sie die Klasse Konstante Ansatz, wenn Sie mit Swift 1.2 oder höher und die verschachtelten struct Ansatz, wenn Sie brauchen, um Unterstützung für frühere Versionen.
Meine Erfahrung mit Swift gibt es drei Ansätze zur Implementierung das Singleton-Muster, unterstützt lazy-Initialisierung und thread-Sicherheit.
Klasse Konstante
Dieser Ansatz unterstützt die verzögerte Initialisierung, weil Swift träge initialisiert die Klasse Konstanten (und-Variablen), und ist thread-sicher durch die definition von
let
. Dies ist nun offiziell empfohlene Weg Instanzierung eines Singletons.Klasse Konstanten eingeführt wurden in Swift 1.2. Wenn Sie brauchen, um Unterstützung für eine frühere version von Swift, verwenden die geschachtelte Struktur struct Ansatz unten oder eine Globale Konstante.
Verschachtelten struct
Hier verwenden wir die statische Konstante der eine geschachtelte Struktur struct wie eine Klasse konstant. Dies ist ein workaround für die fehlende statische Klassen-Konstanten in Swift 1.1 und früher und funktioniert immer noch als ein workaround für das fehlen von statischen Konstanten und Variablen in Funktionen.
dispatch_once
Den traditionellen Objective-C-Ansatz portiert Swift. Ich bin ziemlich sicher, es gibt keinen Vorteil über die geschachtelte Struktur struct Ansatz, aber ich Stelle es hier trotzdem wie ich finde die Unterschiede in der syntax interessant.
Sehen diese GitHub Projekt für unit-tests.
Konstanten sind thread-sicher in allen Sprachen, die ich kenne.
Ich nehme an, Sie sprechen über die letzten Ansatz. Ich zitiere mich selbst: "ich würde sagen, es ist nicht mehr notwendig, verwenden Sie diesen Ansatz, aber ich Stelle es hier trotzdem wie ich finde die Unterschiede in der syntax interessant."
Sollte
init
werden auch deklariert werdenprivate
garantieren eine und nur eine Instanz des Objekts, die es jemals geben wird in der gesamten app Leben?In der "Klasse" ständig " - Ansatz, ich würde vorschlagen, (a) deklarieren Sie die Klasse
final
so dass Sie nicht Unterklasse; und (b) erklären derinit
Methodeprivate
so dass Sie nicht versehentlich instanziieren einer anderen Instanz irgendwo.InformationsquelleAutor hpique
Da Apple hat nun klargestellt, dass static struct-Variablen initialisiert sind beide faul und eingewickelt in dispatch_once (siehe die Anmerkung am Ende der post), ich glaube, dass meine endgültige Lösung sein wird:
Dieser nutzt die automatische faul, thread-sichere Initialisierung von static struct-Elemente, sicher versteckt die eigentliche Implementierung Verbraucher aus, hält alles kompakt unterteilte für die Lesbarkeit und verhindert eine sichtbare Globale variable.
Apple hat klargestellt, dass die lazy-Initialisierung sind thread-sicher, so gibt es keine Notwendigkeit für
dispatch_once
oder ähnlichen SchutzVom hier
Ich möchte hinzufügen, dass eine gute Praxis wäre zu erklären, dass die Initialisierer private:
private init() {}
weiter durchzusetzen, die Tatsache, dass diese Klasse ist nicht dazu gedacht sind, extern instanziert.also static struct var Initialisierung ist faul und thread-sicher, was, wenn das static struct-var ist ein Wörterbuch für multitons, dann müssen wir manuell synchronisieren/queue Anrufe, es ist für jeden Zugriff, richtig?
Wenn ich verstehe deine Frage richtig, Wörterbuch-und array-Zugriffe sind nicht inhärent thread-sicher, so dass Sie brauchen, um mit irgendeiner form der thread-Synchronisierung.
Wie soll ich das nennen, eine Funktion innerhalb dieser singleton-Klasse? Ich brauche eine Funktion, die aufgerufen werden auf dem ersten Aufruf von myClass.sharedInstance.
InformationsquelleAutor
Für den Swift 1.2 und darüber hinaus:
Mit einem Beweis der Richtigkeit (alle Kredit geht hier), es gibt wenig bis keinen Grund, jetzt zu einer der früheren Methoden für singletons.
Update: ist das jetzt die offiziellen Weg zu definieren, singletons, wie beschrieben in der offiziellen docs!
Als für sorgen über die Verwendung
static
vsclass
.static
sollte sein, die eine zu verwenden, auch wennclass
Variablen verfügbar sind. Singletons sind nicht dazu gedacht zu sein, untergeordnet, da führen würden, sind mehrere Instanzen des Basis-singleton. Mitstatic
setzt diese in eine schöne, Swifty Weg.Für Swift 1.0 und 1.1:
Mit den jüngsten Veränderungen in Swift, meist neue access-control-Methoden, jetzt bin ich Neigung in Richtung der sauberere Weg über eine Globale variable für singletons.
Wie bereits erwähnt, in der Swift blog-Artikel hier:
Diese Art der Erstellung eines singleton ist thread-sicher, schnell, faul, und auch überbrückt, um ObjC kostenlos.
ansonsten, das Verhalten ist nicht undefined, es ist nur gebrochen in einer gut definierten Weise: der block wird immer ausgeführt.
In der Dokumentation heißt es, es ist undefiniert. Das aktuelle Verhalten ist daher zufällig.
Das ist eine seltsame Sache zu sagen. Wenn die Dokumentation nennt es "undefined" bedeutet das nur, wer schrieb den code hat keine zusagen machen, um was es tut. Es hat nichts mit dem code zu wissen, wenn die variable statisch ist. Es bedeutet nur, dass die aktuelle (oder scheinbare) Verhalten kann man sich nicht verlassen.
Möchten Sie vielleicht
private init() {}
als initialiser vonSingletonClass
. um zu verhindern, dass instanziieren von außen.InformationsquelleAutor Jack
Swift 1.2 oder höher unterstützt nun statische Variablen/Konstanten in Klassen. So können Sie einfach verwenden einen statischen Konstanten:
InformationsquelleAutor Florian
Gibt es einen besseren Weg, es zu tun. Sie können deklarieren Sie eine Globale variable in der Klasse über die Klasse Unabhängigkeitserklärung wie so
Dieser ruft einfach nur Ihr Standard-init-oder je nachdem, welcher init und Globale Variablen sind dispatch_once standardmäßig in Swift. Dann, in welcher Klasse Sie möchten eine Referenz, die Sie gerade tun:
Also im Grunde, Sie können loszuwerden, der gesamte block der gemeinsamen Instanz-code.
vielleicht könnte ein lassen, ich habe nur getestet mit einer var.
Ich mag diese Antwort, aber ich brauche Zugriff auf diese (Singleton) vom Interface Builder. Jede Idee, wie könnte ich auf diese tpScopeManagerSharedInstance innerhalb der IB?. Danke.-
Dies ist meine bevorzugte Art, mit einem singleton. Es hat all die üblichen Funktionen (thread-Sicherheit & lazy instantiation) und es unterstützt eine sehr leichte syntax: keine Notwendigkeit zu schreiben
TPScopeManager.sharedInstance.doIt()
die ganze Zeit, nur nennen Sie Ihre KlasseTPScopeManagerClass
haben diese Erklärung neben der Klassepublic let TPScopeManager = TPScopeManagerClass()
, und wenn nur schreibenTPScopeManager.doIt()
. Sehr sauber!Es gibt nichts hier, um zu verhindern, Schaffung von zusätzlichen Instanzen von
TPScopeManager
, und es ist daher singleton per definition.InformationsquelleAutor Kris Gellci
Swift singletons ausgesetzt sind, in die Cocoa-frameworks Funktionen der Klasse, z.B.
NSFileManager.defaultManager()
,NSNotificationCenter.defaultCenter()
, also ich finde es macht mehr Sinn, als eine Klasse Funktion zum spiegeln dieses Verhalten, anstatt eine Klasse variable, wie einige andere Lösungen, z.B. mitAbrufen der singleton über
MyClass.sharedInstance()
.die Globale variable sollte geändert werden, um eine Klassenvariable über eine statische innere Klasse.
wenn eine variable als privat gekennzeichnet, jedoch außerhalb einer Klasse, es ist nicht global - sondern Gültigkeitsbereich nur für die Datei ist in. Eine statische innere Klasse Arbeit so ziemlich das gleiche, aber ich habe aktualisiert die Antwort auf die statische, wie Sie vorgeschlagen, wie es besser Gruppen der variable in der Klasse, wenn Sie geschehen, verwenden Sie mehrere Klassen innerhalb der Datei.
"Swift Singletons ausgesetzt sind, in die cocoa-frameworks Funktionen der Klasse" ... Nicht in Swift 3. Sie sind jetzt in der Regel
static
Eigenschaften.InformationsquelleAutor Ryan
Pro die Apple-Dokumentation, es wurde viele Male wiederholt, dass der einfachste Weg, dies zu tun in Swift ist eine statische Eigenschaft type:
Jedoch, wenn Sie auf der Suche nach einem Weg zur weiteren Einrichtung über eine einfache Konstruktor aufrufen, das Geheimnis ist die Verwendung von sofort aufgerufen Verschluss:
Dieser garantiert werden, um thread-sicher und verzögert initialisierten nur einmal.
Sie können nicht, und sollten generell nicht.
InformationsquelleAutor Adrian Macneil
Swift 4+
sollte das private überschreiben Sie die init() {}
InformationsquelleAutor Adam Smaka
Blick auf apples Beispielcode stieß ich auf dieses Muster. Ich bin mir nicht sicher wie Swift befasst sich mit Statik, aber das wäre thread-sicher in C#. Ich umfassen sowohl die Eigenschaft und Methode für Objective-C interop.
leider ist die Statik nur innerhalb von Strukturen, ist also, warum dieses Muster.
Meine Absicht war, dass wir nicht mehr zu verwenden
dispatch_once
stopft. Ich bin mit Wetten auf Ihren Stil. 🙂Ich mag diese Lösung, aber es ist thread-safe?
Nicht
class
innerhalb der Deklaration einer Klasse äquivalentstatic
in einer struct-Deklaration?InformationsquelleAutor user2485100
In kurzen,
Möchten Sie vielleicht zu Lesen Dateien und Initialisierung
InformationsquelleAutor onmyway133
Wenn Sie planen, über die Verwendung Ihrer Swift-singleton-Klasse in Objective-C, bei diesem setup wird der compiler generiert die entsprechenden Objective-C-wie header(s):
Dann in Objective-C class können Sie rufen Sie Ihren singleton die Art und Weise Sie Taten es in der pre-Swift-Tage:
Dies ist nur meine einfache Implementierung.
NSFileManager.defaultManager()
, sondern verwendet immer noch die lazy-thread-safe-statische member-Mechanismen von Swift.Kakao in der Regel setzt diese als statische Eigenschaften, heutzutage, nicht so Klasse funktioniert.
Ich bin mir bewusst, dass mein Kommentar ist über 2 Jahre alt. Vielen Dank für die Erwähnung.
InformationsquelleAutor Michael
Erste Lösung
Später im code:
Zweite Lösung
Und später im code werden Sie in der Lage zu halten, Klammern für weniger Verwirrung:
InformationsquelleAutor Nicolas Manzini
Dann nennen es;
init
alsprivate
, aber auch für die Herstellung dersharedMyModel
alsfinal
! Für die künftigen Leser, Swift 3, könnten wir geneigt sein, zu benennensharedMyModel
einfachshared
.Dies ist die einzig richtige Antwort, außer, dass das überschreiben und aufrufen super.init fehlerhaft und wird nicht mal kompilieren.
InformationsquelleAutor Kemal Can Kaynak
Verwenden:
Wie zu verwenden:
Andere als nicht mit einer globalen Variablen. 🙂
Nein, genau wie einer meiner früheren versuche. Blick auf die edit-history.
InformationsquelleAutor Kingiol
Ist der beste Ansatz in Swift oben 1.2 ist eine Online-singleton, da -
Wissen, mehr Details über diesen Ansatz können Sie besuchen diese link.
NSObject
Unterklasse?. Davon abgesehen, scheint dies im wesentlichen das gleiche wie stackoverflow.com/a/28436202/1187415.InformationsquelleAutor CodeCracker
Ich würde vorschlagen, ein Enum, wie Sie in Java, z.B.:
Können Sie erläutern, wie Sie auf diese Antwort? Es ist mir nicht klar, wo die Singleton instanziiert wird, aus diesem snippet
Ich habe nicht eine Apple developer-login, daher keine schnellen und so kann ich nicht beantworten, wenn die Initialisierung Auftritt. In Java ist es auf den ersten Einsatz. Vielleicht könnten Sie versuchen, es mit einem Druck auf die Initialisierung und sehen, ob der print-tritt beim Start oder nach dem Zugriff. Es wird davon abhängen, wie enum implementiert wird durch den compiler.
Apple hat gerade erläutert, wie dies funktioniert, finden Sie unter developer.apple.com/swift/blog/?id=7. In es, sagen Sie, "führen Sie die Initialisierung für ein globales das erste mal darauf verwiesen wird, ähnlich wie Java" und im besonderen. Sie sagen auch, dass unter der Decke sind Sie mit "dispatch_once, um sicherzustellen, dass die Initialisierung ist unteilbar". Daher enum ist sicherlich der Weg zu gehen, es sei denn, Sie haben einige Lust auf init zu tun, dann einer privaten statischen lassen ist die Lösung.
InformationsquelleAutor Howard Lovatt
Von Apple Docs Swift (3.0.1),
InformationsquelleAutor sleepwalkerfx
Nur als Referenz, hier ist ein Beispiel Singleton-Implementierung von Jack Wu/hpique Verschachtelten Struct Umsetzung. Die Umsetzung zeigt auch, wie die Archivierung arbeiten konnte, sowie einige begleitende Funktionen. Ich konnte nicht finden, dass dieses komplette ein Beispiel, ich hoffe das hilft jemandem!
Und wenn Sie nicht erkennen, einige jener Funktionen, hier ist eine kleine Wohn-Swift-utility-Datei, die ich benutzt habe:
InformationsquelleAutor SchoonSauce
Der einzige richtige Ansatz ist unten
Zugang
Gründe:
static let sharedInstance = Singleton()
wenn Sie nicht möchten, dass zusätzliche setup dann, was Sie sagen, ist richtig.
InformationsquelleAutor applefreak
Ziehe ich diese Umsetzung:
InformationsquelleAutor Viktor Radchenko
Meine Art der Implementierung in Swift...
ConfigurationManager.swift
Zugriff auf die globalDic in einem beliebigen Bildschirm der Anwendung, indem Sie die unten.
Lesen:
Schreiben:
InformationsquelleAutor user2737730
Sehen, nachdem David die Umsetzung, wie es scheint, gibt es keine Notwendigkeit zu haben, eine singleton-Klasse Funktion instanceMethod seit
let
ist schon ziemlich viel die gleiche Sache wie eine sharedInstance-Klasse-Methode. Alles, was Sie tun müssen, ist, deklarieren Sie Sie als Globale Konstante und das wäre es.InformationsquelleAutor Essa A. Haddad
dispatch_once
da statische Initialisierung der Variablen ist faul und automatisch geschützt überdispatch_once
Apple empfiehlt Statik statt dispatch_once aus diesem Grund.InformationsquelleAutor DD.amor
Swift zu erkennen, singleton in der Vergangenheit, ist nichts anderes als die drei Möglichkeiten: Globale Variablen, interne Variablen und dispatch_once Möglichkeiten.
Hier sind zwei gute singleton.(Hinweis: egal, welche Art von schreiben müssen, achten Sie auf die init () - Methode der Privatisierung.Weil in Swift, alle Objekt-Konstruktor standardmäßig ist die öffentlichkeit, die neu geschrieben werden muss init kann sich in private, verhindern, dass andere Objekte von dieser Klasse '()' by default-Initialisierung-Methode das Objekt zu erstellen.)
Methode 1:
Methode 2:
InformationsquelleAutor Tim
Ich kam gerade über das, aber ich mein singleton Vererbung zulassen, und keine dieser Lösungen tatsächlich erlaubt.
Also ich kam mit dieser:
Das Problem mit diesem ersten dirty-Ansatz ist, dass ich kann nicht garantieren, dass Unterklassen implementieren würden die dispatch_once_t und stellen Sie sicher, dass sharedInstanceVar wird nur geändert, einmal pro Klasse...
Werde ich versuchen, zu verfeinern diese weiter, aber es wäre interessant zu sehen, ob jemand hat starke Gefühle, die gegen diese (neben der Tatsache, dass es ist ausführlich und benötigt, um manuell aktualisieren).
InformationsquelleAutor RicardoDuarte
Dies ist die einfachste, die mit einer thread-safe-Funktionen. Kein anderer thread Zugriff auf die gleichen singleton-Objekt, auch wenn Sie wollen. Swift 3/4
InformationsquelleAutor Abhishek Biswas
verwenden Sie eine statische variable und privaten Initialisierer erstellen einer singleton-Klasse.
InformationsquelleAutor Prashant Bhayani
Dies ist meine Umsetzung. Es verhindert auch, dass die Programmierer vom erstellen einer neuen Instanz:
private init
wurde bereits hier vorgeschlagen: stackoverflow.com/a/28436202/1187415.InformationsquelleAutor Yedidya Reiss
InformationsquelleAutor Mike
Ich Neige dazu, verwenden Sie die folgende syntax, da die meisten komplett:
Diese Werke von Swift 1.2 bis zu 4, und bietet mehrere Tugenden:
Singleton.instance
InformationsquelleAutor Gonzalo Durañona