tutorials tutorial example swift grand-central-dispatch swift3 libdispatch

tutorial - ¿A dónde dispatch_once en Swift 3?



nsoperationqueue swift 3 example (7)

Ejemplo para "dispatch_once" en Swift 3.0

Paso 1: solo reemplace el código a continuación 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

Bien, descubrí la nueva API Swifty Dispatch en Xcode 8. Me estoy divirtiendo con DispatchQueue.main.async , y he estado navegando por el módulo Dispatch en Xcode para encontrar todas las nuevas API.

Pero también utilizo 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 ninguna parte del 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.") } }


De acuerdo con la Guía de Migración :

La función gratuita dispatch_once ya no está disponible en Swift. En Swift, puede usar propiedades globales estáticas o propiedades estáticas con inicialización perezosa y obtener la misma seguridad de subprocesos y garantías de llamada única que se proporciona dispatch_once.

Ejemplo:

let myGlobal = { … global contains initialization in a call to a closure … }() // using myGlobal will invoke the initialization code only the first time it is used. _ = myGlobal


Desde Swift 1.x, Swift ha estado utilizando dispatch_once detrás de escena para realizar una inicialización diferida segura de subprocesos de variables globales y propiedades estáticas.

Entonces, la static var anterior ya estaba usando dispatch_once , lo que lo hace un poco extraño (y posiblemente problemático usarlo nuevamente como un token para otro dispatch_once . De hecho, no hay una forma segura de usar dispatch_once sin este tipo de recursión, por lo que obtuvieron deshazte de él. En su lugar, solo usa las características del lenguaje creadas 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") } }

Entonces, todo eso es excelente si ha estado utilizando dispatch_once para una inicialización única que da como resultado algún valor; simplemente puede hacer que ese valor sea la variable global o la propiedad estática que está inicializando.

Pero, ¿qué sucede si está utilizando dispatch_once para hacer un trabajo que no necesariamente tiene un resultado? Todavía puede hacerlo con una variable global o propiedad estática: simplemente haga que el tipo de esa variable sea Void :

let justAOneTimeThing: () = { print("Not coming back here.") }()

Y si acceder a una variable global o una propiedad estática para realizar un trabajo de una sola vez no le parece correcto, por ejemplo, quiere que sus clientes llamen a una función "inicializarme" antes de que trabajen con su biblioteca, simplemente envuelva eso acceso en una función:

func doTheOneTimeThing() { justAOneTimeThing }

Vea la guía de migración para más.


Las otras respuestas aquí y alrededor de las interwebs son bastante buenas, pero creo que este pequeño dato también debe mencionarse:

Lo mejor de dispatch_once fue lo optimizado que estaba, esencialmente eliminando 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 configurar y verificar un token global (real).

Si bien el token podría implementarse razonablemente en Swift, tener que declarar otro booleano almacenado no es tan bueno. Sin mencionar que no es seguro. Como dice el documento , debe usar un "globalizado perezosamente inicializado". Sí, pero ¿por qué abarrotar el alcance global, verdad?

Hasta que alguien me convenza de un método mejor, tiendo a declarar mi cierre de una vez dentro del alcance en el que lo usaré, o lo cerraré razonablemente de la siguiente manera:

private lazy var foo: Void = { // Do this once }()

Básicamente digo que "cuando leo esto, foo debería ser el resultado de ejecutar este bloque". Se comporta exactamente de la misma manera let haría una constante global let , solo en el ámbito correcto. Y más bonita. Luego lo llamaría donde quisiera, al leerlo en algo que nunca se usará de otra manera. Me gusta el _ de Swift por eso. Al igual que:

_ = foo

Esta peculiaridad realmente genial ha existido por un tiempo, pero no ha visto mucho amor. Básicamente, deja la variable sola en el tiempo de ejecución, como un cierre no llamado, hasta que algo quiere ver su resultado Void . En la lectura, llama al cierre, lo tira y mantiene su resultado en foo . Void utiliza prácticamente nada en cuanto a 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 ensamblaje para estar seguro!) ¡Tenga tantos como quiera, y Swift básicamente deja de preocuparse por eso después de la primera ejecución! ¡Pierda ese viejo dispatch_once_t y conserve mucho de su código tan bonito como cuando lo abrió por primera vez el día de Navidad!

Mi único problema es que puedes configurar foo para otra cosa antes de su primera lectura, ¡y luego tu código nunca será llamado! De ahí la constante global let , que impide eso. La cosa es que las constantes en el ámbito de la clase no juegan bien con self , por lo que no se juega con variables de instancia ... Pero en serio, ¿cuándo configuras algo en Void todos modos?

Eso, y necesitaría especificar el tipo de retorno como Void o () , de lo contrario, todavía se quejará de self . Who''da thunk?

Y lazy es solo para hacer que la variable sea tan floja como debería ser, para que Swift no la ejecute directamente en init() .

Bastante elegante, ¡siempre y cuando recuerdes no escribirle! :PAGS


Si bien el patrón "lazy var" me permite dejar de preocuparme por los tokens de despacho y, en general, es más conveniente que dispatch_once() , no me gusta cómo se ve en el sitio de llamadas:

_ = doSomethingOnce

Esperaría que esta declaración se parezca más a una llamada de función (ya que implica acción), pero no lo parece en absoluto. Además, tener que escribir _ = para descartar explícitamente el resultado es innecesario y molesto.

Hay una mejor manera:

lazy var doSomethingOnce: () -> Void = { print("executed once") return {} }()

Lo que hace posible lo siguiente:

doSomethingOnce()

Esto podría ser menos eficiente (ya que llama a un cierre vacío en lugar de simplemente descartar un Void ), pero la claridad mejorada es totalmente valiosa para mí.


Thread-safe dispatch_once:

private lazy var foo: Void = { objc_sync_enter(self) defer { objc_sync_exit(self) } // Do this once }()


Compila bajo Xcode 8 GA Swift 3

La forma recomendada y elegante de crear una instancia de clase individualton 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 puede ser anulada, extendida, también hace que el código sea algo más rápido de ejecutar, menos indirectas.

static let shared = TheRoot (): esta línea realiza un inicio diferido y solo se ejecuta una vez.

Esta solución es segura para subprocesos.