Utilizzo di un modello singleton dispatch_once in Swift


575

Sto cercando di elaborare un modello singleton appropriato per l'utilizzo in Swift. Finora, sono stato in grado di ottenere un modello non thread-safe funzionante come:

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
        }

        if !Static.instance {
            Static.instance = TPScopeManager()
        }

        return Static.instance!
    }
}

L'avvolgimento dell'istanza singleton nella struttura statica dovrebbe consentire una singola istanza che non si scontra con istanze singleton senza schemi di denominazione complessi e dovrebbe rendere le cose abbastanza private. Ovviamente, questo modello non è thread-safe. Quindi ho provato ad aggiungere dispatch_oncea tutto:

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!
    }
}

Ma ottengo un errore del compilatore sulla dispatch_oncelinea:

Impossibile convertire il tipo di espressione 'Void' in type '()'

Ho provato diverse varianti della sintassi, ma sembrano avere tutti gli stessi risultati:

dispatch_once(Static.token, { Static.instance = TPScopeManager() })

Qual è l'uso corretto dell'utilizzo di dispatch_onceSwift? Inizialmente pensavo che il problema fosse dovuto al blocco a causa del ()messaggio di errore, ma più lo guardo, più penso che potrebbe essere una questione di ottenere il dispatch_once_tdefinito correttamente.


3
Vorrei rimuovere tutto quel codice statico e utilizzare una proprietà di sola lettura con un inizializzatore @lazy.
Sulthan,

1
Ecco cosa intendevo. Purtroppo non abbiamo ancora abbastanza informazioni sugli interni. Tuttavia, IMHO qualsiasi implementazione di @lazydovrebbe essere thread-safe.
Sulthan,

1
E in questo modo ha anche il vantaggio di non esporre l'implementazione alle predazioni dei chiamanti.
David Berry,

1
Inoltre non sembra che tu possa avere variabili di classe @lazy.
David Berry,

Stai attento! Due cose da notare con questo approccio. Innanzitutto, tutte le classi che ereditano da questo dovranno sovrascrivere la proprietà sharedInstance. Static.instance = TPScopeManager()forza il tipo di istanza. Se usi qualcosa come Static.instance = self()un inizializzatore richiesto, verrà generata la classe di tipo appropriata. Anche così, e questa è la cosa importante da notare, solo una volta per tutte le istanze nella gerarchia! Il primo tipo da inizializzare è il tipo impostato per tutte le istanze. Non credo che oggettivo c si sia comportato allo stesso modo.
sean woodward,

Risposte:


713

tl; dr: utilizzare l' approccio costante di classe se si utilizza Swift 1.2 o versioni successive e l' approccio nidificato di una struttura se è necessario supportare versioni precedenti.

Dalla mia esperienza con Swift ci sono tre approcci per implementare il modello Singleton che supporta l'inizializzazione lenta e la sicurezza dei thread.

Costante di classe

class Singleton  {
   static let sharedInstance = Singleton()
}

Questo approccio supporta l'inizializzazione lazy perché Swift inizializza pigramente le costanti (e le variabili) della classe ed è sicuro per i thread secondo la definizione di let. Questo è ora il modo ufficialmente raccomandato per creare un'istanza di un singleton.

Le costanti di classe sono state introdotte in Swift 1.2. Se è necessario supportare una versione precedente di Swift, utilizzare l'approccio nidificato di seguito o una costante globale.

Struct annidata

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

Qui stiamo usando la costante statica di una struttura nidificata come costante di classe. Questa è una soluzione alternativa per la mancanza di costanti di classe statiche in Swift 1.1 e precedenti e funziona ancora come soluzione alternativa per la mancanza di costanti statiche e variabili nelle funzioni.

dispatch_once

Il tradizionale approccio Objective-C portato su Swift. Sono abbastanza certo che non ci sia alcun vantaggio rispetto all'approccio struct nidificato, ma lo sto mettendo qui comunque poiché trovo interessanti le differenze nella sintassi.

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!
    }
}

Vedi questo progetto GitHub per unit test.


13
"thread safe in virtù di let" - è stato affermato da qualche parte? Non riesco a trovarne menzione nella documentazione.
jtbandes,

4
Le costanti di @jtbandes sono thread-safe in tutte le lingue che conosco.
hpique

2
@DaveWood Presumo che tu stia parlando dell'ultimo approccio. Cito me stesso: "Direi che non è più necessario utilizzare questo approccio, ma lo sto mettendo qui comunque poiché trovo interessanti le differenze nella sintassi".
hpique

5
Dovrebbe initanche essere dichiarato privateper garantire che esisterà una sola istanza dell'oggetto per tutta la vita dell'app?
Andrew,

5
Nell'approccio "Costante di classe", suggerirei (a) di dichiarare la classe in finalmodo da non sottoclassarla; e (b) dichiarare il initmetodo in privatemodo tale da non poter istanziare accidentalmente un'altra istanza da qualche parte.
Rob,

175

Dato che Apple ha ora chiarito che le variabili di struttura statica sono inizializzate sia in modo pigro che racchiuso dispatch_once(vedi la nota alla fine del post), penso che la mia soluzione finale sarà:

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

        return Singleton.instance
    }
}

Ciò sfrutta l'inizializzazione automatica pigra e thread-safe di elementi di struttura statica, nasconde in modo sicuro l'implementazione effettiva da parte del consumatore, mantiene tutto compattato per la leggibilità ed elimina una variabile globale visibile.

Apple ha chiarito che l'inizializzatore pigro è thread-safe, quindi non è necessario dispatch_onceo protezioni simili

L'inizializzatore lazy per una variabile globale (anche per i membri statici di strutture ed enum) viene eseguito la prima volta che si accede a global e viene avviato come dispatch_once per assicurarsi che l'inizializzazione sia atomica. Ciò consente un modo interessante di utilizzare dispatch_once nel codice: basta dichiarare una variabile globale con un inizializzatore e contrassegnarla come privata.

Da qui


1
Per confermare: le variabili globali hanno un'inizializzazione lazy e thread-safe, ma le variabili di classe no. Giusto?
Bill

14
Aggiungerei che una buona pratica sarebbe quella di dichiarare l'inizializzatore come privato private init() {}:, per rafforzare ulteriormente il fatto che questa classe non è pensata per essere istanziata esternamente.
Pascal Bourque,

1
quindi l'inizializzazione di struct var var è pigra e sicura per i thread, cosa succede se quella struct var var è un dizionario per multitoni, quindi dobbiamo sincronizzare / accodare manualmente le chiamate per ogni accesso, giusto?

Se capisco correttamente la tua domanda, gli accessi al dizionario e agli array non sono intrinsecamente sicuri per i thread, quindi dovrai utilizzare una qualche forma di sincronizzazione dei thread.
David Berry,

@DavidBerry Come devo chiamare una funzione all'interno di questa classe singleton? Ho bisogno di una funzione da chiamare alla prima chiamata di myClass.sharedInstance.
Ameet Dhas,

163

Per Swift 1.2 e oltre:

class Singleton  {
   static let sharedInstance = Singleton()
}

Con una prova di correttezza (tutto il merito va qui ), non c'è quasi nessun motivo per usare uno dei metodi precedenti per i singoli.

Aggiornamento : questo è ora il modo ufficiale per definire i singoli come descritto nei documenti ufficiali !

Per quanto riguarda le preoccupazioni sull'uso staticvs class. staticdovrebbe essere quello da usare anche quando le classvariabili diventano disponibili. I singleton non devono essere suddivisi in sottoclassi poiché ciò comporterebbe più istanze del singleton di base. L'uso staticimpone questo in un modo bellissimo e Swifty.

Per Swift 1.0 e 1.1:

Con le recenti modifiche apportate a Swift, principalmente nuovi metodi di controllo degli accessi, ora mi sto orientando verso un modo più pulito di utilizzare una variabile globale per i singoli.

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

Come menzionato nell'articolo del blog Swift qui :

L'inizializzatore lazy per una variabile globale (anche per i membri statici di strutture ed enum) viene eseguito la prima volta che si accede a global e viene avviato come dispatch_once per assicurarsi che l'inizializzazione sia atomica. Ciò consente un modo interessante di utilizzare dispatch_once nel codice: basta dichiarare una variabile globale con un inizializzatore e contrassegnarla come privata.

Questo modo di creare un singleton è thread-safe, veloce, pigro e anche collegato a ObjC gratuitamente.


2
Chiunque legga solo questa risposta: ricorda di rendere statico il token, altrimenti il ​​comportamento non è definito. Vedi la domanda modificata di David per il codice completo.
nschum,

@nschum altrimenti, il comportamento non è indefinito, è semplicemente rotto in un modo ben definito: il blocco verrà sempre eseguito.
Michael,

@Michael: la documentazione afferma che non è definita. Il comportamento attuale è quindi casuale.
nschum,

1
È una cosa strana da dire. Se la documentazione lo definisce "indefinito", ciò significa che chiunque abbia scritto il codice non promette nulla di ciò che fa. Non ha nulla a che fare con il codice che sa se la variabile è statica. Significa solo che non si può fare affidamento sul comportamento attuale (o apparente).
nschum,

6
Potresti voler aggiungere private init() {}come inizializzatore di SingletonClass. per impedire l'istanza dall'esterno.
Rintaro,

46

Swift 1.2 o versioni successive ora supporta variabili / costanti statiche nelle classi. Quindi puoi semplicemente usare una costante statica:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

35

C'è un modo migliore per farlo. Puoi dichiarare una variabile globale nella tua classe sopra la dichiarazione di classe in questo modo:

var tpScopeManagerSharedInstance = TPScopeManager()

Questo chiama semplicemente il tuo init predefinito o qualunque init e le variabili globali siano dispatch_oncedi default in Swift. Quindi in qualunque classe desideri ottenere un riferimento, fai semplicemente questo:

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

Quindi in pratica puoi eliminare l'intero blocco di codice di istanza condiviso.


3
Perché un "var" e molto un "let"?
Stephan,

1
forse potrebbe essere un let, l'ho provato solo con un var.
Kris Gellci,

Mi piace questa risposta, tuttavia ho bisogno di accedere a questo (Singleton) da Interface Builder. Qualche idea su come posso accedere a questo tpScopeManagerSharedInstance dall'interno di IB ?. Thanks.-
Luis Palacios

Questo è il mio modo preferito di avere un singleton. Ha tutte le solite funzioni (sicurezza dei thread e istanza pigra) e supporta una sintassi molto leggera: non c'è bisogno di scrivere TPScopeManager.sharedInstance.doIt()sempre, basta dare un nome alla classe TPScopeManagerClass, avere questa dichiarazione vicino alla classe public let TPScopeManager = TPScopeManagerClass()e quando si usa semplicemente write TPScopeManager.doIt(). Molto pulito!
Alex

Non c'è nulla qui per impedire la creazione di istanze aggiuntive di TPScopeManager, e quindi non è un singleton per definizione.
Caleb,

28

Singletons Swift sono esposti nei quadri Cocoa come funzioni di classe, ad esempio NSFileManager.defaultManager(), NSNotificationCenter.defaultCenter(). Quindi ha più senso come funzione di classe rispecchiare questo comportamento, piuttosto che una variabile di classe come alcune altre soluzioni. per esempio:

class MyClass {

    private static let _sharedInstance = MyClass()

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

Recupera il singleton via MyClass.sharedInstance().


1
votato per il commento di LearnCocos2D :), anche per lo stile.
x4h1d,

2
la variabile globale dovrebbe essere cambiata in una variabile di classe tramite un statico all'interno della classe.
Malhal,

2
@malhal quando una variabile è contrassegnata come privata ma al di fuori di una classe, non è globale - ma ha solo lo scopo del file in cui si trova. Una statica all'interno della classe funzionerebbe più o meno allo stesso modo, ma ho aggiornato la risposta per usare la statica come hai suggerito, poiché raggruppa meglio la variabile nella classe se ti capita di usare più classi all'interno del file.
Ryan,

1
"Gli Swift Singleton sono esposti nei framework di cacao come funzioni di classe" ... Non in Swift 3. Ora sono di solito staticproprietà.
Rob,

17

Secondo la documentazione Apple , è stato ripetuto più volte che il modo più semplice per farlo in Swift è con una proprietà di tipo statico:

class Singleton {
    static let sharedInstance = Singleton()
}

Tuttavia, se stai cercando un modo per eseguire un'installazione aggiuntiva oltre una semplice chiamata del costruttore, il segreto è utilizzare una chiusura immediatamente invocata:

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

Questo è garantito per essere thread-safe e inizializzato pigramente solo una volta.


come si può impostare l'istanza let su zero?
gpichler,

1
@ user1463853 - Non puoi, e generalmente non dovresti.
Rob,

16

Swift 4+

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

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

2
questo ha bisogno della classe finale, puoi spiegarci meglio la differenza, perché ho problemi con l'altra soluzione di singleton con struct
Raheel Sadiq,

dovrebbe essere override privato init () {}
NSRover il

8

Guardando il codice di esempio di Apple mi sono imbattuto in questo schema. Non sono sicuro di come Swift affronti la statica, ma sarebbe sicuro per i thread in C #. Includo sia la proprietà che il metodo per l'interoperabilità Objective-C.

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

Sono abbastanza sicuro che l'uso di questa sintassi statica predefinita farà tutti i lavori noiosi.
Eonil,

purtroppo la statica funziona solo all'interno di strutture, quindi è per questo motivo.
user2485100,

La mia intenzione era che non dobbiamo usare cose dispatch_once. Scommetto sul tuo stile. :)
Eonil,

classAll'interno di una dichiarazione di classe non è l'equivalente di staticuna dichiarazione di struttura?
Russell Borogove,

@Sam Sì. Vedi il blog di Apple su File e inizializzazione che chiarisce che sia i globali che i membri statici di strutture ed enum beneficiano di questa dispatch_oncecapacità.
Rob,

5

In breve,

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

Potresti voler leggere File e inizializzazione

L'inizializzatore pigro di una variabile globale (anche per membri statici di strutture ed enumerazioni) viene eseguito la prima volta che si accede a globale e viene avviato dispatch_oncein modo da assicurarsi che l'inizializzazione sia atomica.


4

Se stai pianificando di utilizzare la tua classe singleton Swift in Objective-C, questa configurazione farà in modo che il compilatore generi le intestazioni appropriate simili a Objective-C:

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

Quindi nella classe Objective-C puoi chiamare il tuo singleton come facevi nei giorni pre-Swift:

[ImageStore sharedStore];

Questa è solo la mia semplice implementazione.


Questo è in realtà più conciso e corretto rispetto all'altro esempio perché è implementato allo stesso modo di altri singleton Swift. cioè: come funzioni di classe piace NSFileManager.defaultManager(), ma ancora utilizza i pigri meccanismi utente statica thread-safe di Swift.
Leslie Godwin,

Il cacao generalmente li implementa come proprietà statiche, oggigiorno, non come funzioni di classe.
Rob,

Ne sono consapevole, il mio commento ha più di 2 anni. Grazie per averlo menzionato.
Michael,

4

Prima soluzione

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Più avanti nel tuo codice:

func someFunction() {        
    var socketManager = SocketManager        
}

Seconda soluzione

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

class SocketManagerSingleton {

}

E più avanti nel tuo codice sarai in grado di mantenere le parentesi graffe per meno confusione:

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

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

Quindi chiamalo;

let shared = MySingleton.shared

Ben fatto non solo per contrassegnare initcome private, ma anche per creare un sharedMyModelas final! Per il bene dei futuri lettori, in Swift 3, potremmo essere inclini a rinominare sharedMyModelper essere semplicemente shared.
Rob,

Questa è l'unica risposta corretta, tranne per il fatto che l'override e la chiamata a super.init sono errati e non verranno nemmeno compilati.
Michael Morris,

4

Uso:

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!
        }
    }
}

Come usare:

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

Questa è esattamente la stessa di una delle risposte che ho passato sulla strada per la risposta corrente. Poiché le variabili globali sono inizializzate sia pigre che thread-safe, non c'è motivo per la complessità aggiuntiva.
David Berry,

@David Oltre a non avere una variabile globale. :)
hpique

@hpique no, esattamente come uno dei miei precedenti tentativi. Guarda la cronologia delle modifiche.
David Berry,

4

L'approccio migliore in Swift sopra 1.2 è un singleton di una riga, come -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Per conoscere maggiori dettagli su questo approccio puoi visitare questo link .


Perché una NSObjectsottoclasse ?. A parte questo, questo sembra essere essenzialmente lo stesso di stackoverflow.com/a/28436202/1187415 .
Martin R,

3

Da Apple Docs (Swift 3.0.1),

Puoi semplicemente usare una proprietà di tipo statico, che è garantito per essere inizializzato pigramente solo una volta, anche quando si accede contemporaneamente a più thread:

class Singleton {
    static let sharedInstance = Singleton()
}

Se è necessario eseguire un'installazione aggiuntiva oltre all'inizializzazione, è possibile assegnare il risultato dell'invocazione di una chiusura alla costante globale:

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

3

Suggerirei un enum, come useresti in Java, ad es

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO, questo è l'unico modo Swift corretto per implementare Singleton. altre risposte sono ObjC / C / C ++
Bryan Chen,

Potresti approfondire questa risposta? Non mi è chiaro dove sia istanziato Singleton da questo frammento
Kenny Winker,

@KennyWinker Non ho un accesso da sviluppatore Apple, quindi non veloce e quindi non posso rispondere quando si verifica l'inizializzazione. In Java, è al primo utilizzo. Forse potresti provarlo con una stampa all'inizializzazione e vedere se la stampa si verifica all'avvio o dopo l'accesso. Dipenderà da come enum è implementato dal compilatore.
Howard Lovatt,

@KennyWinkler: Apple ha appena chiarito come funziona, vedi developer.apple.com/swift/blog/?id=7 . In esso si dice "esegui l'inizializzatore per un globale la prima volta che viene referenziato, simile a Java" e in particolare. Dicono anche che sotto le copertine stanno usando "dispatch_once per assicurarsi che l'inizializzazione sia atomica". Quindi enum è quasi sicuramente la strada da percorrere a meno che tu non abbia qualche init di fantasia da fare, quindi una soluzione statica privata è la soluzione.
Howard Lovatt,

2

Solo per riferimento, ecco un esempio di implementazione Singleton dell'implementazione Nested Struct di Jack Wu / hpique. L'implementazione mostra anche come l'archiviazione potrebbe funzionare, oltre ad alcune funzioni di accompagnamento. Non ho trovato questo esempio completo, quindi spero che questo aiuti qualcuno!

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)
    }
}

E se non hai riconosciuto alcune di queste funzioni, ecco un piccolo file di utilità Swift vivente che ho usato:

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, puoi creare una classe singleton nel modo seguente:

class AppSingleton: NSObject {

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

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

1

Preferisco questa implementazione:

class APIClient {

}

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

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

1

Il mio modo di implementazione 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")   

}

Accedi a globalDic da qualsiasi schermata dell'applicazione in basso.

Leggere:

 println(ConfigurationManager.sharedInstance.globalDic)  

Scrivi:

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

1

L'unico approccio giusto è sotto.

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

    private init() {}
}

Accedere

let signleton = Singleton.sharedInstance

Motivi:

  • static la proprietà type è garantita per essere inizializzata pigramente solo una volta, anche quando si accede a più thread contemporaneamente, quindi non è necessario utilizzarla dispatch_once
  • Privatizzare il initmetodo in modo che l'istanza non possa essere creata da altre classi.
  • final classe poiché non si desidera che altre classi ereditino la classe Singleton.

Perché hai usato l'inizializzazione di chiusura mentre puoi usarla direttamentestatic let sharedInstance = Singleton()
abhimuralidharan

1
se non si desidera eseguire alcuna configurazione aggiuntiva, ciò che si dice è giusto.
Applefreak

1

Dopo aver visto l'implementazione di David, sembra che non sia necessario avere una funzione di classe singleton instanceMethodpoiché letsta facendo praticamente la stessa cosa di un sharedInstancemetodo di classe. Tutto quello che devi fare è dichiararlo come costante globale e sarebbe così.

let gScopeManagerSharedInstance = ScopeManager()

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

2
Come ho detto nei miei commenti, l'unica ragione per farlo è che ad un certo punto in futuro puoi spostare / nascondere la variabile globale e ottenere un comportamento più singolare. A quel punto, se tutto sta usando un modello coerente, puoi semplicemente cambiare le stesse classi singleton senza dover cambiare l'utilizzo.
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!
}

Come è stato ampiamente discusso qui, non è necessario avvolgere rapidamente l'inizializzazione dispatch_oncepoiché l'inizializzazione delle variabili statiche è pigra e automaticamente protetta tramite dispatch_once Apple consiglia effettivamente di utilizzare statics anziché dispatch_once per questo motivo.
David Berry,

0

Rapido per realizzare singleton in passato, non è altro che i tre modi: variabili globali, variabili interne e modi dispatch_once.

Qui ci sono due buoni singleton. (Nota: non importa quale tipo di scrittura dovrà prestare attenzione al metodo di privatizzazione init (). Perché in Swift, tutto il valore predefinito del costruttore dell'oggetto è pubblico, deve essere riscritto init può essere trasformato in privato , impedisce altri oggetti di questa classe '()' mediante il metodo di inizializzazione predefinito per creare l'oggetto.)

Metodo 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()

Metodo 2:

class AppManager {
    static let sharedInstance = AppManager()

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

// How to use?
AppManager.sharedInstance

-1

Questo è il più semplice con funzionalità thread-safe. Nessun altro thread può accedere allo stesso oggetto singleton anche se lo desidera. 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
Qual è il vantaggio rispetto a una proprietà di tipo statico (che è garantito che sia inizializzato pigramente solo una volta, anche quando si accede a più thread contemporaneamente)?
Martin R,

-1

Ho richiesto al mio singleton di consentire l'ereditarietà, e nessuna di queste soluzioni l'ha permesso. Quindi ho pensato a questo:

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
    }
}
  • In questo modo, quando si esegue Singleton.sharedInstance()prima, verrà restituita l'istanza diSingleton
  • Quando lo fai per SubSingleton.sharedInstance()primo restituirà l'istanza di SubSingletoncreato.
  • Se quanto sopra è fatto, quindi SubSingleton.sharedInstance()è Singletonche è vero e la stessa istanza viene utilizzato.

Il problema con questo primo approccio sporco è che non posso garantire che le sottoclassi implementino dispatch_once_te si assicurino che sharedInstanceVarvenga modificato solo una volta per classe.

Proverò a perfezionarlo ulteriormente, ma sarebbe interessante vedere se qualcuno ha forti sentimenti contro questo (oltre al fatto che è prolisso e richiede di aggiornarlo manualmente).



-2

Uso la sintassi seguente:

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()
    }
}

Funziona da Swift 1.2 fino a 4 e presenta diversi vantaggi:

  1. Ricorda all'utente di non sottoclassare l'implementazione
  2. Impedisce la creazione di istanze aggiuntive
  3. Assicura una creazione pigra e un'istanza unica
  4. Riduce la sintassi (avoids ()) consentendo di accedere all'istanza come Singleton.instance
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.