patrones diseño swift singleton dispatch

diseño - Usando un modelo singleton dispatch_once en Swift



patrones de diseño swift (30)

Estoy tratando de elaborar un modelo singleton apropiado para su uso 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 de singleton en la estructura estática debe permitir una sola instancia que no coincida con las instancias de singleton sin complejos esquemas de nombres, y debe hacer que las cosas sean bastante privadas. Obviamente, sin embargo, este modelo no es seguro para subprocesos, así que intenté agregar dispatch_once a 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 de compilación en la línea dispatch_once :

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

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 correcto de dispatch_once usando Swift? Inicialmente pensé que el problema estaba en el bloqueo debido a () en el mensaje de error, pero cuanto más lo veo, más creo que puede ser una cuestión de definir correctamente la dispatch_once_t .


Acabo de encontrarme con esto, pero le pedí a mi singleton que permitiera la herencia, y ninguna de estas soluciones lo permitió.

Así que 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, se devolverá la instancia de Singleton.
  • Al hacer SubSingleton.sharedInstance () primero, devolverá la instancia de SubSingleton creada.
  • Si se hace lo anterior, entonces SubSingleton.sharedInstance () es Singleton es verdadero y se usa la misma instancia.

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

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


Después de ver la implementación de David, parece que no hay necesidad de tener una función de clase singleton instanceMethod, ya que let hacer casi lo mismo que un método de clase sharedInstance. Todo lo que necesitas 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. }


El único enfoque correcto es abajo

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

Acceder

let signleton = Singleton.sharedInstance

Razones:

  • se garantiza que la propiedad de tipo estático se inicializará solo una vez, incluso cuando se accede a través de múltiples subprocesos simultáneamente, por lo que no es necesario usar dispatch_once
  • Privatizando el método init para que la instancia no pueda ser creada por otras clases.
  • clase final como no quieres que otras clases hereden la clase Singleton

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 puedes visitar este link .


En breve,

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

Es posible que desee leer los here

El inicializador perezoso 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.


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


Hay una mejor manera de hacerlo. Puede declarar una variable global en su clase por encima de la decleración de clase como tal

var tpScopeManagerSharedInstance = TPScopeManager()

Esto solo llama a su inicial por defecto o cualquiera que sea el inicio y las variables globales están dispatch_once por defecto en Swift. Luego, en cualquier clase en la que desee obtener una referencia, simplemente haga esto:

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

Básicamente, puede deshacerse de todo el bloque de código de instancia compartido.


Los singletons de Swift están expuestos en los marcos de Cocoa como funciones de clase, por ejemplo, NSFileManager.defaultManager() , NSNotificationCenter.defaultCenter() , así que creo que tiene más sentido como función de clase reflejar este comportamiento, en lugar de una variable de clase como otras soluciones. uso, por ejemplo

class MyClass { private static let _sharedInstance = MyClass() class func sharedInstance() -> MyClass { return _sharedInstance } }

Recupere el singleton a través de MyClass.sharedInstance() .


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 al globalDic desde cualquier pantalla de la aplicación por el siguiente.

Leer:

println(ConfigurationManager.sharedInstance.globalDic)

Escribir:

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


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

struct StaticRank { static let shared = RankMapping() } class func sharedInstance() -> RankMapping { return StaticRank.shared } class var shared:RankMapping { return StaticRank.shared }


Prefiero esta implementación:

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


Según la documentación de Apple , se ha repetido muchas veces que la forma más sencilla 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 manera de realizar una configuración adicional más allá de una simple llamada del constructor, el secreto es usar un cierre invocado de inmediato:

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

Esto se garantiza que es seguro para subprocesos y que se iniciará perezosamente solo una vez.


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

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

Luego, en la clase de Objective-C, puedes llamar a tu singlet de la forma en que lo hiciste en los días previos a Swift:

[ImageStore sharedStore];

Esta es solo mi implementación simple.


Solo a modo de referencia, aquí hay un ejemplo de implementación de Singleton de la implementación 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() } }


Sugeriría un Enum, como lo usarías en Java, por ejemplo:

enum SharedTPScopeManager: TPScopeManager { case Singleton }


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

class MySingleton { static let sharedMySingleton = MySingleton() private init() { // ... } }


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

Aquí hay dos buenos singleton (nota: no importa qué tipo de escritura debe prestar atención al método de privatización init (). Debido a que en Swift, todo el valor predeterminado del constructor del objeto es público, se debe volver a escribir. Init se puede convertir en privado , prevenga otros objetos de esta clase ''()'' por el método de inicialización por defecto 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


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


Ya que Apple ha aclarado que las variables de estructura estática se inicializan de forma perezosa y se envuelven en dispatch_once (vea la nota al final del post), creo que mi solución final será:

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

Esto se aprovecha de la inicialización perezosa y segura de subprocesos de los elementos estructurales estáticos, oculta de forma segura la implementación real del consumidor, mantiene todo compacto 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_once o protecciones similares

El inicializador perezoso 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 fresca de usar dispatch_once en su código: simplemente declare una variable global con un inicializador y márquela como privada.

Desde here


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

Desde mi experiencia con Swift, hay tres enfoques para implementar el patrón Singleton que admite la inicialización perezosa y la seguridad de subprocesos.

Clase constante

class Singleton { static let sharedInstance = Singleton() }

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

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

Estructura anidada

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

Aquí estamos utilizando 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áticas en Swift 1.1 y anteriores, y aún funciona como una solución para la falta de constantes estáticas y variables en las funciones.

dispatch_once

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 estoy poniendo aquí de todas formas, ya que las diferencias en la sintaxis me parecen interesantes.

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

Ver este proyecto GitHub para pruebas unitarias.


Desde Apple Docs (Swift 3.0.1),

Simplemente puede usar una propiedad de tipo estático, que se garantiza que se inicializará solo una vez, incluso cuando se acceda 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 }() }


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 here ), ahora hay poca o ninguna razón para utilizar cualquiera de los métodos anteriores para singletons.

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

En cuanto a las preocupaciones sobre el uso de static vs class . static debe ser el que se use incluso cuando class variables de class estén disponibles. Los Singletons no están destinados a ser subclasificados, ya que esto daría lugar a múltiples instancias del Singleton base. El uso de la static hace que esto ocurra de una manera hermosa y ágil.

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 inclino por la 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 here :

El inicializador perezoso 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 fresca de usar dispatch_once en su código: simplemente declare una variable global con un inicializador y márquela como privada.

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


Primera solucion

let SocketManager = SocketManagerSingleton(); class SocketManagerSingleton { }

Más adelante en su código:

func someFunction() { var socketManager = SocketManager }

Segunda solucion

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

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

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


Swift 4+

protocol Singleton: class { static var sharedInstance: Self { get } } final class Kraken: Singleton { static let sharedInstance = Kraken() private init() {} }


Tiendo a usar la siguiente sintaxis como la más completa:

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 ofrece varias virtudes:

  1. Recuerda al usuario que no debe implementar subclases
  2. Evita la creación de instancias adicionales.
  3. Asegura una creación perezosa y una instanciación única.
  4. Acorta la sintaxis (evita ()) permitiendo acceder a la instancia como Singleton.instance

use una variable estática y un inicializador privado para crear una clase singleton.

class MySingletonClass { static let sharedSingleton = MySingletonClass() private init() {} }


Esta es mi implementación. También evita que el programador cree una nueva instancia:

let TEST = Test() class Test { private init() { // This is a private (!) constructor } }


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


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

Entonces llámalo;

let shared = MySingleton.shared


private var sharedURLCacheForRequestsKey:Void? extension URLCache{ public static func sharedURLCacheForRequests()->URLCache{ var cache = objc_getAssociatedObject(OperationQueue.main, &sharedURLCacheForRequestsKey) if cache is URLCache { }else{ cache = URLCache(memoryCapacity: 0, diskCapacity: 1*1024*1024*1024, diskPath: "sharedURLCacheForRequestsKey") objc_setAssociatedObject(OperationQueue.main, &sharedURLCacheForRequestsKey, cache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } return cache as! URLCache }}