Verwenden eines displeton_once-Singleton-Modells in Swift


575

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_onceZeile:

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_onceSwift? 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_trichtige Definition zu erhalten.


3
Ich würde all diesen statischen Code entfernen und eine schreibgeschützte Eigenschaft mit einem @ lazy-Initialisierer verwenden.
Sulthan

1
Das ist es was ich meinte. Leider haben wir immer noch nicht genug Informationen über die Interna. IMHO @lazysollte jedoch jede Implementierung von thread-sicher sein.
Sulthan

1
Und dieser Weg hat auch den Vorteil, dass die Implementierung nicht den Raubzügen von Anrufern ausgesetzt wird.
David Berry

1
Es scheint auch nicht so, als könnten Sie @ lazy-Klassenvariablen haben.
David Berry

Achtung! Zwei Dinge, die bei diesem Ansatz zu beachten sind. Zunächst müssen alle Klassen, die davon erben, die Eigenschaft sharedInstance überschreiben. Static.instance = TPScopeManager()erzwingt den Instanztyp. Wenn Sie so etwas wie Static.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.
Sean Woodward

Antworten:


713

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

class Singleton  {
   static let sharedInstance = Singleton()
}

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

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static let instance: Singleton = Singleton()
        }
        return Static.instance
    }
}

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.

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: Singleton? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = Singleton()
        }
        return Static.instance!
    }
}

In diesem GitHub- Projekt finden Sie Unit-Tests.


13
"Thread sicher durch let" - wurde dies irgendwo angegeben? Ich kann es in der Dokumentation nicht erwähnen.
Jtbandes

4
@jtbandes Konstanten sind in allen mir bekannten Sprachen threadsicher.
Hpique

2
@ DaveWood Ich nehme an, Sie sprechen über den letzten Ansatz. Ich zitiere mich selbst: "Ich würde sagen, dass es nicht länger notwendig ist, diesen Ansatz zu verwenden, aber ich setze ihn trotzdem hier ein, da ich die Unterschiede in der Syntax interessant finde."
Hpique

5
Sollte initauch deklariert werden, um privatezu garantieren, dass während der gesamten Lebensdauer der App nur eine Instanz des Objekts vorhanden ist?
Andrew

5
Beim Ansatz "Klassenkonstante" würde ich vorschlagen, (a) die Klasse als solche zu deklarieren, finaldamit Sie sie nicht unterordnen; und (b) Deklarieren der initMethode als privateso, dass Sie nicht versehentlich irgendwo eine andere Instanz instanziieren können.
Rob

175

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:

class WithSingleton {
    class var sharedInstance: WithSingleton {
        struct Singleton {
            static let instance = WithSingleton()
        }

        return Singleton.instance
    }
}

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_onceoder ähnliche Schutzmaßnahmen erforderlich sind

Der verzögerte Initialisierer für eine globale Variable (auch für statische Elemente von Strukturen und Aufzählungen) wird beim ersten Zugriff auf global ausgeführt und als dispatch_once gestartet, um sicherzustellen, dass die Initialisierung atomar ist. Dies ermöglicht eine coole Möglichkeit, dispatch_once in Ihrem Code zu verwenden: Deklarieren Sie einfach eine globale Variable mit einem Initialisierer und markieren Sie sie als privat.

Von hier aus


1
Zur Bestätigung: Globale Variablen haben eine verzögerte, threadsichere Initialisierung, Klassenvariablen jedoch nicht. Recht?
Bill

14
Ich würde hinzufügen, dass eine gute Praxis darin besteht, den Initialisierer als privat zu deklarieren: private init() {}um die Tatsache weiter zu verstärken, dass diese Klasse nicht dazu gedacht ist, extern instanziiert zu werden.
Pascal Bourque

1
Die Initialisierung der statischen Strukturvariablen ist also faul und threadsicher. Wenn diese statische Strukturvariable ein Wörterbuch für Multitons ist, müssen wir die Aufrufe für jeden Zugriff manuell synchronisieren / in die Warteschlange stellen, oder?

Wenn ich Ihre Frage richtig verstehe, sind Wörterbuch- und Array-Zugriffe nicht von Natur aus threadsicher, sodass Sie eine Form der Thread-Synchronisierung verwenden müssen.
David Berry

@DavidBerry Wie soll ich eine Funktion innerhalb dieser Singleton-Klasse aufrufen? Beim ersten Aufruf von myClass.sharedInstance muss eine Funktion aufgerufen werden.
Ameet Dhas

163

Für Swift 1.2 und höher:

class Singleton  {
   static let sharedInstance = Singleton()
}

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 staticvs class. staticsollte auch dann verwendet werden, wenn classVariablen verfügbar werden. Singletons sind nicht als Unterklassen gedacht, da dies zu mehreren Instanzen des Basis-Singletons führen würde. Die Verwendung staticerzwingt 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.

private let _singletonInstance = SingletonClass()
class SingletonClass {
  class var sharedInstance: SingletonClass {
    return _singletonInstance
  }
}

Wie im Swift-Blog-Artikel hier erwähnt :

Der verzögerte Initialisierer für eine globale Variable (auch für statische Elemente von Strukturen und Aufzählungen) wird beim ersten Zugriff auf global ausgeführt und als dispatch_once gestartet, um sicherzustellen, dass die Initialisierung atomar ist. Dies ermöglicht eine coole Möglichkeit, dispatch_once in Ihrem Code zu verwenden: Deklarieren Sie einfach eine globale Variable mit einem Initialisierer und markieren Sie sie als privat.

Diese Art, einen Singleton zu erstellen, ist threadsicher, schnell, faul und wird auch kostenlos mit ObjC verbunden.


2
Jeder, der nur diese Antwort liest: Denken Sie daran, das Token statisch zu machen, da sonst das Verhalten undefiniert ist. Den vollständigen Code finden Sie in Davids bearbeiteter Frage.
Nschum

@nschum sonst ist das Verhalten nicht undefiniert, es ist nur auf eine genau definierte Weise gebrochen: Der Block wird immer ausgeführt.
Michael

@ Michael: Die Dokumentation besagt, dass es undefiniert ist. Das aktuelle Verhalten ist daher zufällig.
Nschum

1
Das ist seltsam zu sagen. Wenn die Dokumentation es "undefiniert" nennt, bedeutet dies nur, dass derjenige, der den Code geschrieben hat, keine Versprechungen macht, was er tut. Es hat nichts damit zu tun, dass der Code weiß, ob die Variable statisch ist. Es bedeutet nur, dass man sich nicht auf das aktuelle (oder offensichtliche) Verhalten verlassen kann.
Nschum

6
Möglicherweise möchten Sie private init() {}als Initialisierer von hinzufügen SingletonClass. um zu verhindern, dass von außen instanziiert wird.
Rintaro

46

Swift 1.2 oder höher unterstützt jetzt statische Variablen / Konstanten in Klassen. Sie können also einfach eine statische Konstante verwenden:

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}

35

Es gibt einen besseren Weg, dies zu tun. Sie können eine globale Variable in Ihrer Klasse über der Klassendeklaration wie folgt deklarieren:

var tpScopeManagerSharedInstance = TPScopeManager()

Dies ruft nur Ihren Standard-Init auf oder welche Init- und globalen Variablen dispatch_oncein Swift standardmäßig sind. Dann tun Sie in der Klasse, in der Sie eine Referenz erhalten möchten, einfach Folgendes:

var refrence = tpScopeManagerSharedInstance
// or you can just access properties and call methods directly
tpScopeManagerSharedInstance.someMethod()

Grundsätzlich können Sie also den gesamten Block des gemeinsam genutzten Instanzcodes entfernen.


3
Warum ein "var" und viel ein "let"?
Stephan

1
Vielleicht könnte es eine Vermietung sein, ich habe es nur mit einem Var getestet.
Kris Gellci

Ich mag diese Antwort, muss jedoch über Interface Builder auf diese (Singleton) zugreifen. Haben Sie eine Idee, wie ich von IB aus auf diese tpScopeManagerSharedInstance zugreifen kann?. Danke.-
Luis Palacios

Dies ist meine bevorzugte Art, einen Singleton zu haben. Es verfügt über alle üblichen Funktionen (Thread-Sicherheit und verzögerte Instanziierung) und unterstützt eine sehr einfache Syntax: Sie müssen nicht TPScopeManager.sharedInstance.doIt()ständig schreiben , benennen Sie einfach Ihre Klasse TPScopeManagerClass, haben Sie diese Deklaration neben der Klasse public let TPScopeManager = TPScopeManagerClass()und verwenden Sie einfach schreiben TPScopeManager.doIt(). Sehr sauber!
Alex

Hier gibt es nichts, was die Erstellung zusätzlicher Instanzen von verhindern könnte TPScopeManager, und es handelt sich daher per Definition nicht um einen Singleton .
Caleb

28

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:

class MyClass {

    private static let _sharedInstance = MyClass()

    class func sharedInstance() -> MyClass {
        return _sharedInstance
    }
}

Rufen Sie den Singleton über ab MyClass.sharedInstance().


1
für den Kommentar von LearnCocos2D :), auch für den Stil.
x4h1d

2
Die globale Variable sollte über eine Statik innerhalb der Klasse in eine Klassenvariable geändert werden.
Malhal

2
@malhal Wenn eine Variable als privat, aber außerhalb einer Klasse markiert ist, ist sie nicht global - sondern nur für die Datei, in der sie sich befindet. Eine Statik innerhalb der Klasse würde fast genauso funktionieren, aber ich habe die Antwort aktualisiert, um die Statik zu verwenden Wie Sie vorgeschlagen haben, gruppiert es die Variable besser in der Klasse, wenn Sie zufällig mehrere Klassen in der Datei verwenden.
Ryan

1
"Swift Singletons werden in den Kakao-Frameworks als Klassenfunktionen verfügbar gemacht" ... Nicht in Swift 3. Sie sind jetzt normalerweise staticEigenschaften.
Rob

17

Gemäß der Apple-Dokumentation wurde mehrfach wiederholt, dass der einfachste Weg, dies in Swift zu tun, eine statische Typ-Eigenschaft ist:

class Singleton {
    static let sharedInstance = Singleton()
}

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:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

Dies ist garantiert threadsicher und wird nur einmal träge initialisiert.


Wie können Sie die statische let-Instanz auf nil zurücksetzen?
Gpichler

1
@ user1463853 - Sie können und sollten im Allgemeinen nicht.
Rob

16

Swift 4+

protocol Singleton: class {
    static var sharedInstance: Self { get }
}

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}

2
Dies braucht die letzte Klasse, können Sie den Unterschied näher erläutern, denn ich habe Probleme mit der anderen Lösung von Singleton mit Struktur
Raheel Sadiq

sollte das privat sein override init () {}
NSRover

8

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.

struct StaticRank {
    static let shared = RankMapping()
}

class func sharedInstance() -> RankMapping {
    return StaticRank.shared
}

class var shared:RankMapping {
    return StaticRank.shared
}

Ich bin mir ziemlich sicher, dass nur die Verwendung dieser statischen Standardsyntax die nervigen Aufgaben erledigt.
Eonil

Leider funktioniert die Statik nur innerhalb von Strukturen, deshalb dieses Muster.
user2485100

Meine Absicht war, dass wir keine dispatch_onceSachen benutzen müssen . Ich wette auf deinen Stil. :)
Eonil

classEntspricht eine Klassendeklaration nicht staticeiner Strukturdeklaration?
Russell Borogove

@ Sam Ja, das ist es. Lesen Sie den Apple-Blogeintrag zu Dateien und Initialisierung, in dem deutlich wird, dass sowohl globale als auch statische Mitglieder von Strukturen und Aufzählungen von dieser dispatch_onceFunktion profitieren .
Rob

5

In Kürze,

class Manager {
    static let sharedInstance = Manager()
    private init() {}
}

Möglicherweise möchten Sie Dateien und Initialisierung lesen

Der verzögerte Initialisierer für eine globale Variable (auch für statische Elemente von Strukturen und Aufzählungen) wird beim ersten Zugriff auf global ausgeführt und gestartet dispatch_once, um sicherzustellen, dass die Initialisierung atomar ist.


4

Wenn Sie planen, Ihre Swift-Singleton-Klasse in Objective-C zu verwenden, generiert der Compiler bei diesem Setup die entsprechenden Objective-C-ähnlichen Header:

class func sharedStore() -> ImageStore {
struct Static {
    static let instance : ImageStore = ImageStore()
    }
    return Static.instance
}

Dann können Sie in der Objective-C-Klasse Ihren Singleton so nennen, wie Sie es in Tagen vor Swift getan haben:

[ImageStore sharedStore];

Dies ist nur meine einfache Implementierung.


Dies ist tatsächlich prägnanter und korrekter als das andere Beispiel, da es genauso implementiert ist wie andere Swift-Singletons. dh: als Klasse funktioniert wie NSFileManager.defaultManager(), verwendet aber immer noch die faulen thread-sicheren statischen Member-Mechanismen von Swift.
Leslie Godwin

Kakao implementiert diese heutzutage im Allgemeinen als statische Eigenschaften, nicht als Klassenfunktionen.
Rob

Mir ist das bewusst, mein Kommentar ist über 2 Jahre alt. Vielen Dank für die Erwähnung.
Michael

4

Erste Lösung

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Später in Ihrem Code:

func someFunction() {        
    var socketManager = SocketManager        
}

Zweite Lösung

func SocketManager() -> SocketManagerSingleton {
    return _SocketManager
}
let _SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Und später in Ihrem Code können Sie Klammern für weniger Verwirrung behalten:

func someFunction() {        
    var socketManager = SocketManager()        
}

4
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

Dann nenne es;

let shared = MySingleton.shared

Gut gemacht, um nicht nur initals zu markieren private, sondern auch um das sharedMyModelals zu machen final! Für zukünftige Leser könnten wir in Swift 3 dazu neigen, sharedMyModeleinfach umzubenennen shared.
Rob

Dies ist die einzig richtige Antwort, außer dass das Überschreiben und der Aufruf von super.init fehlerhaft sind und nicht einmal kompiliert werden.
Michael Morris

4

Verwenden:

class UtilSingleton: NSObject {

    var iVal: Int = 0

    class var shareInstance: UtilSingleton {
        get {
            struct Static {
                static var instance: UtilSingleton? = nil
                static var token: dispatch_once_t = 0
            }
            dispatch_once(&Static.token, {
                Static.instance = UtilSingleton()
            })
            return Static.instance!
        }
    }
}

Wie benutzt man:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")

Dies ist genau das Gleiche wie eine der Antworten, die ich auf dem Weg zur aktuellen Antwort durchlaufen habe. Da globale Variablen sowohl faul als auch threadsicher initialisiert werden, gibt es keinen Grund für die zusätzliche Komplexität.
David Berry

@ David Abgesehen davon, dass keine globale Variable vorhanden ist. :)
hpique

@hpique nein, genau wie bei einem meiner früheren Versuche. Sehen Sie sich den Bearbeitungsverlauf an.
David Berry

4

Der beste Ansatz in Swift über 1.2 ist ein einzeiliger Singleton, da -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Um mehr über diesen Ansatz zu erfahren, können Sie diesen Link besuchen .


Warum eine NSObjectUnterklasse? Abgesehen davon scheint dies im Wesentlichen dasselbe zu sein wie stackoverflow.com/a/28436202/1187415 .
Martin R

3

Aus Apple Docs (Swift 3.0.1),

Sie können einfach eine statische Typ-Eigenschaft verwenden, die garantiert nur einmal träge initialisiert wird, selbst wenn über mehrere Threads gleichzeitig zugegriffen wird:

class Singleton {
    static let sharedInstance = Singleton()
}

Wenn Sie über die Initialisierung hinaus zusätzliche Einstellungen vornehmen müssen, können Sie das Ergebnis des Aufrufs eines Abschlusses der globalen Konstante zuordnen:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

3

Ich würde ein vorschlagen enum, wie Sie es in Java verwenden würden, z

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO, dies ist der einzig richtige Swift-Weg, um Singleton zu implementieren. andere Antworten sind ObjC / C / C ++ Weg
Bryan Chen

Könnten Sie diese Antwort näher erläutern? Mir ist nicht klar, wo Singleton aus diesem Ausschnitt instanziiert wird
Kenny Winker

@KennyWinker Ich habe kein Apple-Entwickler-Login, daher kein schnelles und kann daher nicht antworten, wenn die Initialisierung erfolgt. In Java wird es zum ersten Mal verwendet. Vielleicht können Sie es mit einem Ausdruck bei der Initialisierung versuchen und sehen, ob der Druck beim Start oder nach dem Zugriff erfolgt. Dies hängt davon ab, wie enum vom Compiler implementiert wird.
Howard Lovatt

@KennyWinkler: Apple hat gerade klargestellt, wie dies funktioniert, siehe developer.apple.com/swift/blog/?id=7 . Darin heißt es "Führen Sie den Initialisierer beim ersten Referenzieren für ein globales Element aus, ähnlich wie bei Java" und insbesondere. Sie sagen auch, dass sie unter dem Deckmantel "dispatch_once verwenden, um sicherzustellen, dass die Initialisierung atomar ist". Daher ist enum mit ziemlicher Sicherheit der richtige Weg, es sei denn, Sie haben etwas Besonderes zu tun, dann ist eine private statische Vermietung die Lösung.
Howard Lovatt

2

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!

import Foundation

class ItemStore: NSObject {

    class var sharedStore : ItemStore {
        struct Singleton {
            // lazily initiated, thread-safe from "let"
            static let instance = ItemStore()
        }
        return Singleton.instance
    }

    var _privateItems = Item[]()
    // The allItems property can't be changed by other objects
    var allItems: Item[] {
        return _privateItems
    }

    init() {
        super.init()
        let path = itemArchivePath
        // Returns "nil" if there is no file at the path
        let unarchivedItems : AnyObject! = NSKeyedUnarchiver.unarchiveObjectWithFile(path)

        // If there were archived items saved, set _privateItems for the shared store equal to that
        if unarchivedItems {
            _privateItems = unarchivedItems as Array<Item>
        } 

        delayOnMainQueueFor(numberOfSeconds: 0.1, action: {
            assert(self === ItemStore.sharedStore, "Only one instance of ItemStore allowed!")
        })
    }

    func createItem() -> Item {
        let item = Item.randomItem()
        _privateItems.append(item)
        return item
    }

    func removeItem(item: Item) {
        for (index, element) in enumerate(_privateItems) {
            if element === item {
                _privateItems.removeAtIndex(index)
                // Delete an items image from the image store when the item is 
                // getting deleted
                ImageStore.sharedStore.deleteImageForKey(item.itemKey)
            }
        }
    }

    func moveItemAtIndex(fromIndex: Int, toIndex: Int) {
        _privateItems.moveObjectAtIndex(fromIndex, toIndex: toIndex)
    }

    var itemArchivePath: String {
        // Create a filepath for archiving
        let documentDirectories = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
        // Get the one document directory from that list
        let documentDirectory = documentDirectories[0] as String
        // append with the items.archive file name, then return
        return documentDirectory.stringByAppendingPathComponent("items.archive")
    }

    func saveChanges() -> Bool {
        let path = itemArchivePath
        // Return "true" on success
        return NSKeyedArchiver.archiveRootObject(_privateItems, toFile: path)
    }
}

Und wenn Sie einige dieser Funktionen nicht erkannt haben, finden Sie hier eine kleine lebende Swift-Dienstprogrammdatei, die ich verwendet habe:

import Foundation
import UIKit

typealias completionBlock = () -> ()

extension Array {
    func contains(#object:AnyObject) -> Bool {
        return self.bridgeToObjectiveC().containsObject(object)
    }

    func indexOf(#object:AnyObject) -> Int {
        return self.bridgeToObjectiveC().indexOfObject(object)
    }

    mutating func moveObjectAtIndex(fromIndex: Int, toIndex: Int) {
        if ((fromIndex == toIndex) || (fromIndex > self.count) ||
            (toIndex > self.count)) {
                return
        }
        // Get object being moved so it can be re-inserted
        let object = self[fromIndex]

        // Remove object from array
        self.removeAtIndex(fromIndex)

        // Insert object in array at new location
        self.insert(object, atIndex: toIndex)
    }
}

func delayOnMainQueueFor(numberOfSeconds delay:Double, action closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue()) {
            closure()
    }
}

2

In Swift können Sie eine Singleton-Klasse folgendermaßen erstellen:

class AppSingleton: NSObject {

    //Shared instance of class
    static let sharedInstance = AppSingleton()

    override init() {
        super.init()
    }
}

1

Ich bevorzuge diese Implementierung:

class APIClient {

}

var sharedAPIClient: APIClient = {
    return APIClient()
}()

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}

1

Meine Art der Implementierung in Swift ...

ConfigurationManager.swift

import Foundation

    let ConfigurationManagerSharedInstance = ConfigurationManager()
 class ConfigurationManager : NSObject {
    var globalDic: NSMutableDictionary = NSMutableDictionary()

class var sharedInstance:ConfigurationManager {
    return ConfigurationManagerSharedInstance

}

init() {

    super.init()

    println ("Config Init been Initiated, this will be called only onece irrespective of many calls")   

}

Greifen Sie von jedem Bildschirm der Anwendung aus wie folgt auf globalDic zu.

Lesen:

 println(ConfigurationManager.sharedInstance.globalDic)  

Schreiben:

 ConfigurationManager.sharedInstance.globalDic = tmpDic // tmpDict is any value that to be shared among the application

1

Der einzig richtige Ansatz ist unten.

final class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code if anything
        return instance
    }()

    private init() {}
}

Zugreifen

let signleton = Singleton.sharedInstance

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 ist dispatch_once
  • Durch die Privatisierung der initMethode kann keine Instanz von anderen Klassen erstellt werden.
  • final Klasse, da Sie nicht möchten, dass andere Klassen die Singleton-Klasse erben.

Warum haben Sie die Abschlussinitialisierung verwendet, während Sie direkt verwenden könnenstatic let sharedInstance = Singleton()
abhimuralidharan

1
Wenn Sie keine zusätzlichen Einstellungen vornehmen möchten, ist das, was Sie sagen, richtig.
Applefreak

1

Nachdem Sie Davids Implementierung gesehen haben, scheint es nicht erforderlich zu sein, eine Singleton-Klassenfunktion zu haben, instanceMethodda letdies fast dasselbe tut wie eine sharedInstanceKlassenmethode. Alles was Sie tun müssen, ist es als globale Konstante zu deklarieren und das wäre es.

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
   // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}

2
Wie ich in meinen Kommentaren sage, ist der einzige Grund dafür, dass Sie irgendwann in der Zukunft die globale Variable verschieben / ausblenden und ein singletonähnlicheres Verhalten erzielen können. Wenn zu diesem Zeitpunkt alles ein konsistentes Muster verwendet, können Sie die Singleton-Klassen einfach selbst ändern, ohne die Verwendung ändern zu müssen.
David Berry

0
   func init() -> ClassA {
    struct Static {
        static var onceToken : dispatch_once_t = 0
        static var instance : ClassA? = nil
    }

    dispatch_once(&Static.onceToken) {
        Static.instance = ClassA()
    }

    return Static.instance!
}

Wie hier ausführlich besprochen wurde, ist es nicht erforderlich, die Initialisierung schnell einzuschließen, da die Initialisierung dispatch_oncestatischer Variablen verzögert ist und automatisch über dispatch_once Apple geschützt wird. Aus diesem Grund wird empfohlen, anstelle von dispatch_once statische Daten zu verwenden .
David Berry

0

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:

class AppManager {
    private static let _sharedInstance = AppManager()

    class func getSharedInstance() -> AppManager {
       return _sharedInstance
    }

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.getSharedInstance()

Methode 2:

class AppManager {
    static let sharedInstance = AppManager()

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.sharedInstance

-1

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

struct DataService {

    private static var _instance : DataService?

    private init() {}   //cannot initialise from outer class

    public static var instance : DataService {
        get {
            if _instance == nil {
                DispatchQueue.global().sync(flags: .barrier) {
                    if _instance == nil {
                        _instance = DataService()
                    }
                }
            }
            return _instance!
        }
    }
}

2
Was ist der Vorteil gegenüber einer statischen Typ-Eigenschaft (die garantiert nur einmal träge initialisiert wird, selbst wenn über mehrere Threads gleichzeitig zugegriffen wird)?
Martin R

-1

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:

public class Singleton {
    private static var sharedInstanceVar = Singleton()

    public class func sharedInstance() -> Singleton {
        return sharedInstanceVar
    }
}


public class SubSingleton: Singleton {

    private static var sharedInstanceToken: dispatch_once_t = 0

    public class override func sharedInstance() -> SubSingleton {
        dispatch_once(&sharedInstanceToken) {
            sharedInstanceVar = SubSingleton()
        }
    return sharedInstanceVar as! SubSingleton
    }
}
  • Auf diese Weise wird beim Singleton.sharedInstance()ersten Ausführen die Instanz von zurückgegebenSingleton
  • Wenn Sie dies SubSingleton.sharedInstance()zuerst tun , wird die Instanz von zurückgegebenSubSingleton .
  • Wenn das oben genannte getan wird, SubSingleton.sharedInstance()ist Singletones 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_tund 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).



-2

Ich benutze die folgende Syntax:

public final class Singleton {    
    private class func sharedInstance() -> Singleton {
        struct Static {
            //Singleton instance.
            static let sharedInstance = Singleton()
        }
        return Static.sharedInstance
    }

    private init() { }

    class var instance: Singleton {
        return sharedInstance()
    }
}

Dies funktioniert von Swift 1.2 bis 4 und hat mehrere Vorteile:

  1. Erinnert den Benutzer daran, die Implementierung nicht zu unterklassifizieren
  2. Verhindert die Erstellung zusätzlicher Instanzen
  3. Gewährleistet eine verzögerte Erstellung und eine einzigartige Instanziierung
  4. Verkürzt die Syntax (vermeidet ()), indem der Zugriff auf die Instanz als ermöglicht wird Singleton.instance
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.