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_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 del compilador en la dispatch_once
lí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_once
Swift? 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_t
definirlo correctamente.
@lazy
debe ser segura para subprocesos.Static.instance = TPScopeManager()
fuerza el tipo de instancia. Si usa algo comoStatic.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.Respuestas:
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
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
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.
Vea este proyecto de GitHub para pruebas unitarias.
fuente
init
También se debe declararprivate
que garantiza que solo existirá una instancia del objeto a lo largo de la vida útil de la aplicación?final
para que no la subclasifique; y (b) declarar que elinit
método esprivate
para que no pueda crear una instancia accidental de otra instancia en alguna parte.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á: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_once
protecciones similaresDesde aqui
fuente
private init() {}
para reforzar aún más el hecho de que esta clase no debe ser instanciada externamente.Para Swift 1.2 y más allá:
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
static
vsclass
.static
debe ser el que se debe usar incluso cuando lasclass
variables 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 usostatic
hace 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.
Como se menciona en el artículo del blog Swift aquí :
Esta forma de crear un singleton es segura para subprocesos, rápida, perezosa y también enlazada a ObjC de forma gratuita.
fuente
private init() {}
como inicializador deSingletonClass
. para evitar instanciar desde el exterior.Swift 1.2 o posterior ahora admite variables / constantes estáticas en las clases. Entonces puedes usar una constante estática:
fuente
Hay una mejor manera de hacerlo. Puede declarar una variable global en su clase sobre la declaración de clase de esta manera:
Esto solo llama a su init predeterminado o cualquier variable init y global que esté
dispatch_once
por defecto en Swift. Luego, en cualquier clase que desee obtener una referencia, simplemente haga esto:Así que básicamente puedes deshacerte de todo el bloque de código de instancia compartido.
fuente
TPScopeManager.sharedInstance.doIt()
todo el tiempo, solo tiene que nombrar su claseTPScopeManagerClass
, tener esta declaración al lado de la clasepublic let TPScopeManager = TPScopeManagerClass()
, y cuando se usa solo escribirTPScopeManager.doIt()
. ¡Muy limpio!TPScopeManager
y, por lo tanto, no es un singleton por definición.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:Recupere el singleton vía
MyClass.sharedInstance()
.fuente
static
propiedades.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:
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:
Esto está garantizado para ser seguro para subprocesos y vagamente inicializado solo una vez.
fuente
Swift 4+
fuente
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.
fuente
dispatch_once
cosas. Estoy apostando por tu estilo. :)class
dentro de una declaración de clase el equivalente destatic
en una declaración de estructura?dispatch_once
capacidad.En breve,
Es posible que desee leer archivos e inicialización
fuente
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:
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:
Esta es solo mi implementación simple.
fuente
NSFileManager.defaultManager()
, pero todavía utiliza los mecanismos miembro estático de hilo de seguridad de descanso de Swift.Primera solución
Más adelante en su código:
Segunda solución
Y más adelante en su código podrá mantener llaves para menos confusión:
fuente
Entonces llámalo;
fuente
init
comoprivate
, sino también por hacer elsharedMyModel
asfinal
! Por el bien de los futuros lectores, en Swift 3, podríamos estar inclinados a cambiar el nombresharedMyModel
para que sea simpleshared
.Utilizar:
Cómo utilizar:
fuente
El mejor enfoque en Swift por encima de 1.2 es un singleton de una línea, como:
Para conocer más detalles sobre este enfoque, puede visitar este enlace .
fuente
NSObject
subclase? Aparte de eso, esto parece ser esencialmente lo mismo que stackoverflow.com/a/28436202/1187415 .De Apple Docs (Swift 3.0.1),
fuente
Sugeriría un
enum
, como lo usaría en Java, por ejemplofuente
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!
Y si no reconoció algunas de esas funciones, aquí hay un pequeño archivo de utilidad Swift vivo que he estado usando:
fuente
En swift, puede crear una clase singleton de la siguiente manera:
fuente
Prefiero esta implementación:
fuente
Mi forma de implementación en Swift ...
ConfigurationManager.swift
Acceda a globalDic desde cualquier pantalla de la aplicación de la siguiente manera.
Leer:
Escribir:
fuente
El único enfoque correcto está por debajo.
Acceder
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 usardispatch_once
init
método para que otras clases no puedan crear la instancia.final
clase ya que no desea que otras clases hereden la clase Singleton.fuente
static let sharedInstance = Singleton()
Después de ver la implementación de David, parece que no hay necesidad de tener una función de clase singleton
instanceMethod
ya quelet
está haciendo más o menos lo mismo que unsharedInstance
método de clase. Todo lo que necesita hacer es declararlo como una constante global y eso sería todo.fuente
fuente
dispatch_once
ya que la inicialización de variables estáticas es perezosa y está protegida automáticamente a través dedispatch_once
Apple, en realidad recomienda el uso de estadísticas en lugar de dispatch_once por esa razón.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:
Método 2:
fuente
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
fuente
Requerí que mi singleton permitiera la herencia, y ninguna de estas soluciones realmente lo permitió. Entonces se me ocurrió esto:
Singleton.sharedInstance()
primero, devolverá la instancia deSingleton
SubSingleton.sharedInstance()
primero devolverá la instancia deSubSingleton
created.SubSingleton.sharedInstance()
esSingleton
es 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_t
y 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).
fuente
Esta es mi implementación. También evita que el programador cree una nueva instancia:
fuente
private init
ya se sugirió aquí: stackoverflow.com/a/28436202/1187415 .Yo uso la siguiente sintaxis:
Esto funciona desde Swift 1.2 hasta 4, y tiene varias ventajas:
Singleton.instance
fuente