Usar un modelo singleton dispatch_once en Swift


575

Estoy tratando de encontrar un modelo singleton apropiado para usar en Swift. Hasta ahora, he podido obtener un modelo seguro sin subprocesos que funciona como:

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

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

        return Static.instance!
    }
}

Ajustar la instancia singleton en la estructura Estática debería permitir una instancia única que no colisione con instancias singleton sin esquemas de nombres complejos, y debería hacer que las cosas sean bastante privadas. Obviamente, este modelo no es seguro para subprocesos. Así que traté de agregar dispatch_oncea todo el asunto:

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

Pero me sale un error del compilador en la dispatch_oncelínea:

No se puede convertir el tipo de expresión 'Void' a tipo '()'

He probado varias variantes diferentes de la sintaxis, pero todas parecen tener los mismos resultados:

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

¿Cuál es el uso adecuado del uso de dispatch_onceSwift? Inicialmente pensé que el problema estaba en el bloque debido al ()mensaje de error, pero cuanto más lo miro, más creo que puede ser una cuestión de dispatch_once_tdefinirlo correctamente.


3
Quitaría todo ese código estático y usaría una propiedad de solo lectura con un inicializador @lazy.
Sulthan

1
A eso me refería. Lamentablemente, todavía no tenemos suficiente información sobre las partes internas. Sin embargo, en mi humilde opinión, cualquier implementación @lazydebe ser segura para subprocesos.
Sulthan

1
Y de esta manera también tiene la ventaja de no exponer la implementación a las depredaciones de quienes llaman.
David Berry

1
Tampoco parece que pueda tener variables de clase @lazy.
David Berry

¡Ten cuidado! Dos cosas a tener en cuenta con este enfoque. Primero, cualquier clase que herede de esto tendrá que anular la propiedad sharedInstance. Static.instance = TPScopeManager()fuerza el tipo de instancia. Si usa algo como Static.instance = self()con un inicializador requerido, se generará la clase de tipo apropiada. Aun así, y esto es lo importante a tener en cuenta, ¡solo una vez para todas las instancias en la jerarquía! El primer tipo que se inicializa es el conjunto de tipos para todas las instancias. No creo que el objetivo-c se haya comportado igual.
Sean Woodward

Respuestas:


713

tl; dr: utilice el enfoque de clase constante si está utilizando Swift 1.2 o superior y el enfoque de estructura anidada si necesita admitir versiones anteriores.

Según mi experiencia con Swift, existen tres enfoques para implementar el patrón Singleton que admiten la inicialización diferida y la seguridad de subprocesos.

Constante de clase

class Singleton  {
   static let sharedInstance = Singleton()
}

Este enfoque admite la inicialización diferida porque Swift inicializa perezosamente las constantes de clase (y variables), y es seguro para subprocesos según la definición de let. Esta es ahora la forma oficialmente recomendada de instanciar un singleton.

Las constantes de clase se introdujeron en Swift 1.2. Si necesita admitir una versión anterior de Swift, use el siguiente enfoque de estructura anidada o una constante global.

Estructura anidada

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

Aquí estamos usando la constante estática de una estructura anidada como una constante de clase. Esta es una solución para la falta de constantes de clase estática en Swift 1.1 y versiones anteriores, y aún funciona como una solución para la falta de constantes estáticas y variables en las funciones.

despacho_una vez

El enfoque tradicional de Objective-C portado a Swift. Estoy bastante seguro de que no hay ninguna ventaja sobre el enfoque de estructura anidada, pero lo pongo aquí de todos modos porque encuentro interesantes las diferencias en la sintaxis.

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

Vea este proyecto de GitHub para pruebas unitarias.


13
"hilo seguro en virtud de let": ¿se ha dicho esto en alguna parte? No puedo encontrar mención de ello en la documentación.
jtbandes

44
@jtbandes Las constantes son seguras para subprocesos en todos los idiomas que conozco.
hpique

2
@DaveWood Supongo que estás hablando del último enfoque. Me citaré a mí mismo: "Diría que ya no es necesario usar este enfoque, pero lo estoy poniendo aquí de todos modos porque encuentro interesantes las diferencias en la sintaxis".
hpique

55
¿ initTambién se debe declarar privateque garantiza que solo existirá una instancia del objeto a lo largo de la vida útil de la aplicación?
Andrew

55
En el enfoque "Constante de clase", sugeriría (a) declarar que la clase es finalpara que no la subclasifique; y (b) declarar que el initmétodo es privatepara que no pueda crear una instancia accidental de otra instancia en alguna parte.
Rob

175

Dado que Apple ahora ha aclarado que las variables de estructura estática se inicializan tanto de forma diferida como envuelta dispatch_once(vea la nota al final de la publicación), creo que mi solución final será:

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

        return Singleton.instance
    }
}

Esto aprovecha la inicialización automática perezosa y segura de subprocesos de elementos de estructura estática, oculta de forma segura la implementación real del consumidor, mantiene todo compartimentado de forma compacta para la legibilidad y elimina una variable global visible.

Apple ha aclarado que el inicializador perezoso es seguro para subprocesos, por lo que no hay necesidad de dispatch_onceprotecciones similares

El inicializador diferido para una variable global (también para miembros estáticos de estructuras y enumeraciones) se ejecuta la primera vez que se accede a global, y se inicia como dispatch_once para asegurarse de que la inicialización sea atómica. Esto permite una forma genial de usar dispatch_once en su código: simplemente declare una variable global con un inicializador y márquelo como privado.

Desde aqui


1
Para confirmar: las variables globales tienen una inicialización perezosa y segura para subprocesos, pero las variables de clase no. ¿Derecha?
Bill

14
Añadiría que una buena práctica sería declarar el inicializador como privado: private init() {}para reforzar aún más el hecho de que esta clase no debe ser instanciada externamente.
Pascal Bourque

1
así que la inicialización de la estructura estática var es perezosa y segura para subprocesos, ¿qué pasa si esa estructura estática var es un diccionario para multitonos, entonces tenemos que sincronizar / poner en cola manualmente las llamadas para cada acceso, verdad?

Si entiendo su pregunta correctamente, los accesos de diccionario y matriz no son inherentemente seguros para subprocesos, por lo que deberá usar alguna forma de sincronización de subprocesos.
David Berry

@DavidBerry ¿Cómo debo llamar a una función dentro de esta clase singleton? Necesito que se invoque una función en la primera llamada de myClass.sharedInstance.
Ameet Dhas

163

Para Swift 1.2 y más allá:

class Singleton  {
   static let sharedInstance = Singleton()
}

Con una prueba de corrección (todo el crédito va aquí ), ahora hay poca o ninguna razón para usar cualquiera de los métodos anteriores para los singletons.

Actualización : ¡Esta es ahora la forma oficial de definir singletons como se describe en los documentos oficiales !

En cuanto a las preocupaciones sobre el uso staticvs class. staticdebe ser el que se debe usar incluso cuando las classvariables estén disponibles. Los singletons no están destinados a ser subclase ya que eso daría lugar a múltiples instancias del singleton base. El uso statichace cumplir esto de una manera hermosa y rápida.

Para Swift 1.0 y 1.1:

Con los cambios recientes en Swift, en su mayoría nuevos métodos de control de acceso, ahora me estoy inclinando hacia una forma más limpia de usar una variable global para singletons.

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

Como se menciona en el artículo del blog Swift aquí :

El inicializador diferido para una variable global (también para miembros estáticos de estructuras y enumeraciones) se ejecuta la primera vez que se accede a global, y se inicia como dispatch_once para asegurarse de que la inicialización sea atómica. Esto permite una forma genial de usar dispatch_once en su código: simplemente declare una variable global con un inicializador y márquelo como privado.

Esta forma de crear un singleton es segura para subprocesos, rápida, perezosa y también enlazada a ObjC de forma gratuita.


2
Cualquiera que lea solo esta respuesta: Recuerde hacer que el token sea estático, de lo contrario, el comportamiento es indefinido. Vea la pregunta editada de David para el código completo.
nschum

@nschum de lo contrario, el comportamiento no está indefinido, solo se rompe de una manera bien definida: el bloque siempre se ejecutará.
Michael

@ Michael: La documentación indica que no está definido. El comportamiento actual es, por lo tanto, casual.
nschum

1
Eso es algo extraño que decir. Si la documentación lo llama "indefinido", eso significa que quien escribió el código no promete lo que hace. No tiene nada que ver con el código sabiendo si la variable es estática. Simplemente significa que no se puede confiar en el comportamiento actual (o aparente).
nschum

66
Es posible que desee agregar private init() {}como inicializador de SingletonClass. para evitar instanciar desde el exterior.
rintaro

46

Swift 1.2 o posterior ahora admite variables / constantes estáticas en las clases. Entonces puedes usar una constante estática:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

35

Hay una mejor manera de hacerlo. Puede declarar una variable global en su clase sobre la declaración de clase de esta manera:

var tpScopeManagerSharedInstance = TPScopeManager()

Esto solo llama a su init predeterminado o cualquier variable init y global que esté dispatch_oncepor defecto en Swift. Luego, en cualquier clase que desee obtener una referencia, simplemente haga esto:

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

Así que básicamente puedes deshacerte de todo el bloque de código de instancia compartido.


3
¿Por qué un "var" y mucho un "let"?
Stephan

1
tal vez podría ser un let, solo lo probé con una var.
Kris Gellci

Me gusta esta respuesta, sin embargo, necesito acceder a esta (Singleton) desde Interface Builder. ¿Alguna idea de cómo podría acceder a este tpScopeManagerSharedInstance desde IB? Gracias.-
Luis Palacios

Esta es mi forma preferida de tener un singleton. Tiene todas las características habituales (seguridad de subprocesos e instanciación perezosa) y admite una sintaxis muy liviana: no es necesario escribir TPScopeManager.sharedInstance.doIt()todo el tiempo, solo tiene que nombrar su clase TPScopeManagerClass, tener esta declaración al lado de la clase public let TPScopeManager = TPScopeManagerClass(), y cuando se usa solo escribir TPScopeManager.doIt(). ¡Muy limpio!
Alex

No hay nada aquí para evitar la creación de instancias adicionales TPScopeManagery, por lo tanto, no es un singleton por definición.
Caleb

28

Singletons Swift están expuestos en los marcos de cacao como funciones de la clase, por ejemplo NSFileManager.defaultManager(), NSNotificationCenter.defaultCenter(). Por lo tanto, tiene más sentido como una función de clase para reflejar este comportamiento, en lugar de una variable de clase como algunas otras soluciones. p.ej:

class MyClass {

    private static let _sharedInstance = MyClass()

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

Recupere el singleton vía MyClass.sharedInstance().


1
votó por el comentario de LearnCocos2D :), también por el estilo.
x4h1d

2
la variable global debe cambiarse a una variable de clase a través de una estática dentro de la clase.
malhal 01 de

2
@malhal cuando una variable se marca como privada pero fuera de una clase, no es global, sino que solo se enfoca en el archivo en el que se encuentra. Una estática dentro de la clase funcionaría más o menos igual, pero he actualizado la respuesta para usar la estática como sugirió, ya que agrupa mejor la variable a la clase si utiliza múltiples clases dentro del archivo.
Ryan

1
"Los Singletons Swift están expuestos en los marcos de cacao como funciones de clase" ... No en Swift 3. Ahora son generalmente staticpropiedades.
Rob

17

Según la documentación de Apple , se ha repetido muchas veces que la forma más fácil de hacer esto en Swift es con una propiedad de tipo estático:

class Singleton {
    static let sharedInstance = Singleton()
}

Sin embargo, si está buscando una forma de realizar una configuración adicional más allá de una simple llamada de constructor, el secreto es usar un cierre invocado de inmediato:

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

Esto está garantizado para ser seguro para subprocesos y vagamente inicializado solo una vez.


¿Cómo puede configurar la instancia de let estática de nuevo a cero?
gpichler

1
@ user1463853 - No puedes, y generalmente no deberías.
Rob

16

Swift 4+

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

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

2
esto tiene clase final, se puede explicar más la diferencia, coz tengo problema con la otra solución de producto único con estructura
Raheel Sadiq

debería ser privado invalidar init () {}
NSRover

8

Mirando el código de muestra de Apple me encontré con este patrón. No estoy seguro de cómo Swift trata las estadísticas, pero esto sería seguro para subprocesos en C #. Incluyo tanto la propiedad como el método para la interoperabilidad Objective-C.

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

Estoy bastante seguro de que solo usar esta sintaxis estática predeterminada hará todos los trabajos molestos.
Eonil

desafortunadamente, la estática solo funciona dentro de las estructuras, por eso este patrón.
user2485100

Mi intención era no tener que usar dispatch_oncecosas. Estoy apostando por tu estilo. :)
Eonil

¿No es classdentro de una declaración de clase el equivalente de staticen una declaración de estructura?
Russell Borogove

@ Sam Sí, lo es. Consulte la entrada del blog de Apple sobre Archivos e inicialización, que deja en claro que tanto los miembros globales como los miembros estáticos de estructuras y enumeraciones se benefician de esta dispatch_oncecapacidad.
Rob

5

En breve,

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

Es posible que desee leer archivos e inicialización

El inicializador diferido para una variable global (también para miembros estáticos de estructuras y enumeraciones) se ejecuta la primera vez que se accede a global y se inicia dispatch_oncepara asegurarse de que la inicialización sea atómica.


4

Si está planeando usar su clase de singleton Swift en Objective-C, esta configuración hará que el compilador genere los encabezados apropiados de tipo Objective-C:

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

Luego, en la clase Objective-C, puede llamar a su singleton de la manera en que lo hizo en los días previos a Swift:

[ImageStore sharedStore];

Esta es solo mi implementación simple.


Esto es en realidad más conciso y correcto que el otro ejemplo porque se implementa de la misma manera que otros singletons Swift. es decir: como funciones de clase gusta NSFileManager.defaultManager(), pero todavía utiliza los mecanismos miembro estático de hilo de seguridad de descanso de Swift.
Leslie Godwin el

El cacao generalmente los implementa como propiedades estáticas, hoy en día, no como funciones de clase.
Rob

Soy consciente de eso, mi comentario tiene más de 2 años. Gracias por mencionar
Michael

4

Primera solución

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Más adelante en su código:

func someFunction() {        
    var socketManager = SocketManager        
}

Segunda solución

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

class SocketManagerSingleton {

}

Y más adelante en su código podrá mantener llaves para menos confusión:

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

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

Entonces llámalo;

let shared = MySingleton.shared

¡Bien hecho por no solo marcar initcomo private, sino también por hacer el sharedMyModelas final! Por el bien de los futuros lectores, en Swift 3, podríamos estar inclinados a cambiar el nombre sharedMyModelpara que sea simple shared.
Rob

Esta es la única respuesta correcta, excepto que la anulación y la llamada a super.init son erróneas y ni siquiera se compilarán.
Michael Morris

4

Utilizar:

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

Cómo utilizar:

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

Esto es exactamente lo mismo que una de las respuestas que pasé en el camino a la respuesta actual. Dado que las variables globales se inicializan tanto perezosas como seguras para subprocesos, no hay razón para la complejidad adicional.
David Berry

@David Aparte de no tener una variable global. :)
hpique

@hpique no, exactamente como uno de mis intentos anteriores. Mira el historial de edición.
David Berry

4

El mejor enfoque en Swift por encima de 1.2 es un singleton de una línea, como:

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Para conocer más detalles sobre este enfoque, puede visitar este enlace .


¿Por qué una NSObjectsubclase? Aparte de eso, esto parece ser esencialmente lo mismo que stackoverflow.com/a/28436202/1187415 .
Martin R

3

De Apple Docs (Swift 3.0.1),

Simplemente puede usar una propiedad de tipo estático, que se garantiza que se inicializa perezosamente solo una vez, incluso cuando se accede a través de varios subprocesos simultáneamente:

class Singleton {
    static let sharedInstance = Singleton()
}

Si necesita realizar una configuración adicional más allá de la inicialización, puede asignar el resultado de la invocación de un cierre a la constante global:

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

3

Sugeriría un enum, como lo usaría en Java, por ejemplo

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

En mi opinión, esta es la única forma correcta de Swift para implementar Singleton. otras respuestas son ObjC / C / C ++
Bryan Chen

¿Podría dar más detalles sobre esta respuesta? No está claro para mí dónde se instancia Singleton de este fragmento
Kenny Winker

@KennyWinker No tengo un inicio de sesión de desarrollador de Apple, por lo tanto, no soy rápido y, por lo tanto, no puedo responder cuando se produce la inicialización. En Java, se usa por primera vez. Quizás podría intentarlo con una impresión en la inicialización y ver si la impresión ocurre en el inicio o después del acceso. Dependerá de cómo el compilador implemente enum.
Howard Lovatt

@KennyWinkler: Apple acaba de aclarar cómo funciona esto, consulte developer.apple.com/swift/blog/?id=7 . En él dicen "ejecuta el inicializador para un global la primera vez que se hace referencia, similar a Java" y en particular. También dicen que debajo de las cubiertas están usando "dispatch_once para asegurarse de que la inicialización sea atómica". Por lo tanto, enum es casi seguro el camino a seguir a menos que tenga que hacer un inicio sofisticado, entonces un let estático privado es la solución.
Howard Lovatt

2

Solo como referencia, aquí hay un ejemplo de implementación Singleton de la implementación de Nested Struct de Jack Wu / hpique. La implementación también muestra cómo podría funcionar el archivado, así como algunas funciones que lo acompañan. No pude encontrar este ejemplo completo, ¡así que espero que esto ayude a alguien!

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

Y si no reconoció algunas de esas funciones, aquí hay un pequeño archivo de utilidad Swift vivo que he estado usando:

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

En swift, puede crear una clase singleton de la siguiente manera:

class AppSingleton: NSObject {

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

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

1

Prefiero esta implementación:

class APIClient {

}

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

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

1

Mi forma de implementación en 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")   

}

Acceda a globalDic desde cualquier pantalla de la aplicación de la siguiente manera.

Leer:

 println(ConfigurationManager.sharedInstance.globalDic)  

Escribir:

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

1

El único enfoque correcto está por debajo.

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

    private init() {}
}

Acceder

let signleton = Singleton.sharedInstance

Razones:

  • static Se garantiza que la propiedad de tipo se inicialice perezosamente solo una vez, incluso cuando se accede a través de varios subprocesos simultáneamente, por lo que no es necesario usar dispatch_once
  • Privatizar el initmétodo para que otras clases no puedan crear la instancia.
  • final clase ya que no desea que otras clases hereden la clase Singleton.

¿Por qué usó la inicialización de cierre mientras puede usar directamente?static let sharedInstance = Singleton()
abhimuralidharan

1
Si no desea hacer ninguna configuración adicional, entonces lo que dice es correcto.
applefreak

1

Después de ver la implementación de David, parece que no hay necesidad de tener una función de clase singleton instanceMethodya que letestá haciendo más o menos lo mismo que un sharedInstancemétodo de clase. Todo lo que necesita hacer es declararlo como una constante global y eso sería todo.

let gScopeManagerSharedInstance = ScopeManager()

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

2
Como digo en mis comentarios, la única razón para hacerlo es que en algún momento en el futuro puede mover / ocultar la variable global y obtener un comportamiento más único. En ese punto, si todo está usando un patrón consistente, puede cambiar las clases singleton sin tener que cambiar el uso.
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!
}

Como se ha discutido extensamente aquí, no es necesario ajustar rápidamente la inicialización dispatch_onceya que la inicialización de variables estáticas es perezosa y está protegida automáticamente a través de dispatch_once Apple, en realidad recomienda el uso de estadísticas en lugar de dispatch_once por esa razón.
David Berry

0

Rápido para darse cuenta de singleton en el pasado, no es más que las tres formas: variables globales, variables internas y formas de despacho.

Aquí hay dos buenos singleton. (Nota: no importa qué tipo de escritura deba prestar atención al método de privatización init (). Debido a que en Swift, todo el constructor predeterminado del objeto es público, debe reescribirse init puede convertirse en privado , evitar otros objetos de esta clase '()' por defecto método de inicialización para crear el objeto.)

Método 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()

Método 2:

class AppManager {
    static let sharedInstance = AppManager()

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

// How to use?
AppManager.sharedInstance

-1

Este es el más simple con capacidades seguras para subprocesos. Ningún otro hilo puede acceder al mismo objeto singleton, incluso si lo desean. 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
¿Cuál es la ventaja sobre una propiedad de tipo estático (que se garantiza que se inicializa perezosamente solo una vez, incluso cuando se accede a través de varios subprocesos simultáneamente)?
Martin R

-1

Requerí que mi singleton permitiera la herencia, y ninguna de estas soluciones realmente lo permitió. Entonces se me ocurrió esto:

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
    }
}
  • De esta manera, al hacer Singleton.sharedInstance()primero, devolverá la instancia deSingleton
  • Al hacer SubSingleton.sharedInstance()primero devolverá la instancia deSubSingleton created.
  • Si se hace lo anterior, a continuación, SubSingleton.sharedInstance()es Singletones verdadera y se utiliza la misma instancia.

El problema con este primer enfoque sucio es que no puedo garantizar que las subclases implementen dispatch_once_ty me asegure de quesharedInstanceVar solo se modifique una vez por clase.

Intentaré refinar esto aún más, pero sería interesante ver si alguien tiene fuertes sentimientos en contra de esto (además del hecho de que es detallado y requiere actualizarlo manualmente).



-2

Yo uso la siguiente sintaxis:

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

Esto funciona desde Swift 1.2 hasta 4, y tiene varias ventajas:

  1. Le recuerda al usuario que no subclase la implementación
  2. Impide la creación de instancias adicionales
  3. Asegura una creación perezosa y una instanciación única
  4. Acorta la sintaxis (evita ()) al permitir acceder a la instancia como Singleton.instance
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.