De acuerdo, descubrí la nueva API de Swifty Dispatch en Xcode 8. Me estoy divirtiendo usando DispatchQueue.main.async
, y he estado navegando por el módulo Dispatch
en Xcode para encontrar todas las nuevas API.
Pero también uso dispatch_once
para asegurarme de que cosas como la creación de singleton y la configuración de una sola vez no se ejecuten más de una vez (incluso en un entorno multiproceso) … y dispatch_once
no se encuentra en el nuevo módulo Dispatch.
static var token: dispatch_once_t = 0 func whatDoYouHear() { print("All of this has happened before, and all of it will happen again.") dispatch_once(&token) { print("Except this part.") } }
Desde Swift 1.x, Swift ha estado utilizando dispatch_once
detrás de las escenas para realizar una inicialización diferida segura para subprocesos de variables globales y propiedades estáticas.
Así que la static var
anterior ya estaba usando dispatch_once
, lo que hace que sea un poco raro (y posiblemente problemático usarlo de nuevo como un token para otro dispatch_once
. De hecho, realmente no hay una manera segura de usar dispatch_once
sin este tipo de recursión, así que obtuvieron deshacerse de él. En su lugar, simplemente use las características del lenguaje integradas en él:
// global constant: SomeClass initializer gets called lazily, only on first use let foo = SomeClass() // global var, same thing happens here // even though the "initializer" is an immediately invoked closure var bar: SomeClass = { let b = SomeClass() b.someProperty = "whatever" b.doSomeStuff() return b }() // ditto for static properties in classes/structures/enums class MyClass { static let singleton = MyClass() init() { print("foo") } }
Así que eso es genial si ha estado utilizando dispatch_once
para la inicialización única que da como resultado algún valor; puede simplemente convertir ese valor en la variable global o la propiedad estática que está inicializando.
Pero, ¿qué sucede si usa dispatch_once
para hacer un trabajo que no necesariamente tiene un resultado? Todavía puedes hacer eso con una variable global o propiedad estática: simplemente haz que el tipo de esa variable sea Void
:
let justAOneTimeThing: () = { print("Not coming back here.") }()
Y si acceder a una variable global o propiedad estática para realizar un trabajo por única vez no le parece correcto, por ejemplo, desea que sus clientes llamen a una función de “inicialización” antes de que trabajen con su biblioteca. acceso en una función:
func doTheOneTimeThing() { justAOneTimeThing }
Consulte la guía de migración para obtener más información.
Las otras respuestas aquí y alrededor de las interwebs son geniales, pero creo que este pequeño tidbit también debería mencionarse:
Lo bueno de dispatch_once
fue lo optimizado que era, esencialmente rechazando el código después de la primera ejecución de una manera que apenas entiendo, pero estoy razonablemente seguro de que sería mucho más rápido que establecer y verificar una ficha (real) global.
Si bien el token podría implementarse razonablemente en Swift, tener que declarar otro booleano almacenado no es tan bueno. Por no mencionar que no es seguro. Como dice el documento , debe usar un “global inicializado de forma perezosa”. Sí, pero ¿por qué complicar el scope global, verdad?
Hasta que alguien me convenza de un método mejor, tiendo a declarar mi cierre do-one dentro del scope en el que lo usaré, o razonablemente cerca de él, de la siguiente manera:
private lazy var foo: Void = { // Do this once }()
Básicamente estoy diciendo que “cuando leo esto, foo
debería ser el resultado de ejecutar este bloque”. Se comporta de la misma manera que una constante de let
global, solo en el scope correcto. Y más bonito. Entonces lo llamaría donde quisiera, leyéndolo en algo que de otro modo nunca se usará. Me gusta Swift’s _
por eso. Al igual que:
_ = foo
Este capricho realmente genial ha existido desde hace un tiempo, pero no ha visto mucho amor. Básicamente, deja la variable sola en tiempo de ejecución, como un cierre no llamado, hasta que algo quiera ver su resultado Void
. Al leer, llama al cierre, lo descarta y mantiene su resultado en foo
. Void
utiliza prácticamente nada en la memoria, por lo que las lecturas posteriores (es decir, _ = foo
) no hacen nada en la CPU. (¡No me cite sobre eso, alguien por favor revise el ensamble para estar seguro!) ¡Tenga tantas como quiera, y Swift básicamente deja de preocuparse por eso después de la primera carrera! Pierde ese viejo dispatch_once_t
y mantén una gran cantidad de código tan bonito como cuando lo abriste el día de Navidad.
Mi único problema es que puedes establecer foo
en otra cosa antes de su primera lectura, ¡y luego nunca se invocará tu código! De ahí la constante global, que impide eso. La cosa es que las constantes en el scope de la clase no funcionan bien con self
, así que no juegas con variables de instancia … Pero en serio, ¿cuándo configuras algo para Void
todos modos?
Eso, y necesitaría especificar el tipo de devolución como Void
o ()
, de lo contrario, seguirá quejándose de self
. Who’da thunk?
Y lazy
es solo para hacer que la variable sea tan floja como debería ser, por lo que Swift no la ejecuta directamente en init()
.
Bastante elegante, ¡siempre y cuando recuerde que no debe escribir! :PAG
// Singleton Class class Singleton: NSObject { var strSample = NSString() static let sharedInstance:Singleton = { let instance = Singleton () return instance } () // MARK: Init override init() { print("My Class Initialized") // initialized with variable or property strSample = "My String" } }
Imagen de muestra Singleton
// ViewController.swift override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let mySingleton = Singleton.sharedInstance print(mySingleton.strSample) mySingleton.strSample = "New String" print(mySingleton.strSample) let mySingleton1 = Singleton.sharedInstance print(mySingleton1.strSample) }
Imagen de muestra de ViewController
My Class Initialized My String New String New String
Comstack bajo Xcode 8 GA Swift 3
La forma recomendada y elegante de crear una instancia de clase singleton dispatch_once:
final class TheRoot { static let shared = TheRoot() var appState : AppState = .normal ...
Para usarlo:
if TheRoot.shared.appState == .normal { ... }
¿Qué hacen estas líneas?
final, por lo que la clase no se puede sobrescribir, ampliar, también hace que el código sea más rápido de ejecutar, menos indirecciones.
static let shared = TheRoot () – Esta línea hace init lazy, y solo se ejecuta una vez.
Esta solución es segura para hilos.