¿Hacia dónde despacha_unance en Swift 3?

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

Ejemplo para “dispatch_once” en Swift 3.0

Paso 1: Simplemente reemplace el código siguiente con su Singleton.swift (clase Singleton)

 // 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

Paso 2: llame a Singleton desde ViewController.swift

 // 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

Salida como esta

 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.