example dispatchqueue dispatch_async central async swift grand-central-dispatch

dispatchqueue - nsoperationqueue swift 3 example



dispatch_once después de los cambios de Swift 3 GCD API (8)

Ampliando la respuesta anterior de Tod Cunningham, agregué otro método que hace que el token sea automáticamente de archivo, función y línea.

public extension DispatchQueue { private static var _onceTracker = [String]() public class func once(file: String = #file, function: String = #function, line: Int = #line, block: () -> Void) { let token = "/(file):/(function):/(line)" once(token: token, block: block) } /** Executes a block of code, associated with a unique token, only once. The code is thread safe and will only execute the code once even in the presence of multithreaded calls. - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID - parameter block: Block to execute once */ public class func once(token: String, block: () -> Void) { objc_sync_enter(self) defer { objc_sync_exit(self) } guard !_onceTracker.contains(token) else { return } _onceTracker.append(token) block() } }

Entonces puede ser más simple llamar:

DispatchQueue.once { setupUI() }

y aún puede especificar un token si lo desea:

DispatchQueue.once(token: "com.hostname.project") { setupUI() }

Supongo que podría tener una colisión si tiene el mismo archivo en dos módulos. Lástima que no haya #module

¿Cuál es la nueva sintaxis para dispatch_once en Swift después de los cambios realizados en la versión de idioma 3? La versión anterior era la siguiente.

var token: dispatch_once_t = 0 func test() { dispatch_once(&token) { } }

Estos son los cambios realizados en libdispatch .


Del doc :

Envío
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 las mismas garantías de seguridad de subprocesos y llamadas únicas una vez que se proporciona dispatch_once. Ejemplo:

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


Editar

Respuesta de @ Frizlab: no se garantiza que esta solución sea segura para subprocesos. Se debe usar una alternativa si esto es crucial

La solución simple es

lazy var dispatchOnce : Void = { // or anyName I choose self.title = "Hello Lazy Guy" return }()

utilizado como

override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() _ = dispatchOnce }


Puede declarar una función variable de nivel superior como esta:

private var doOnce: ()->() = { /* do some work only once per instance */ return {} }()

entonces llame a esto en cualquier lugar:

doOnce()


Si bien el uso de globales globalizados perezosos puede tener sentido para una inicialización única, no tiene sentido para otros tipos. Tiene mucho sentido usar globales perezosos inicializados para cosas como singletons, no tiene mucho sentido para cosas como proteger una configuración de swizzle.

Aquí hay una implementación de estilo Swift 3 de dispatch_once:

public extension DispatchQueue { private static var _onceTracker = [String]() /** Executes a block of code, associated with a unique token, only once. The code is thread safe and will only execute the code once even in the presence of multithreaded calls. - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID - parameter block: Block to execute once */ public class func once(token: String, block:@noescape(Void)->Void) { objc_sync_enter(self); defer { objc_sync_exit(self) } if _onceTracker.contains(token) { return } _onceTracker.append(token) block() } }

Aquí hay un ejemplo de uso:

DispatchQueue.once(token: "com.vectorform.test") { print( "Do This Once!" ) }

o usando un UUID

private let _onceToken = NSUUID().uuidString DispatchQueue.once(token: _onceToken) { print( "Do This Once!" ) }

Como actualmente estamos en un momento de transición de swift 2 a 3, aquí hay un ejemplo de implementación de swift 2:

public class Dispatch { private static var _onceTokenTracker = [String]() /** Executes a block of code, associated with a unique token, only once. The code is thread safe and will only execute the code once even in the presence of multithreaded calls. - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID - parameter block: Block to execute once */ public class func once(token token: String, @noescape block:dispatch_block_t) { objc_sync_enter(self); defer { objc_sync_exit(self) } if _onceTokenTracker.contains(token) { return } _onceTokenTracker.append(token) block() } }


Swift 3: para aquellos a quienes les gustan las clases (o estructuras) reutilizables:

public final class /* struct */ DispatchOnce { private var lock: OSSpinLock = OS_SPINLOCK_INIT private var isInitialized = false public /* mutating */ func perform(block: (Void) -> Void) { OSSpinLockLock(&lock) if !isInitialized { block() isInitialized = true } OSSpinLockUnlock(&lock) } }

Uso:

class MyViewController: UIViewController { private let /* var */ setUpOnce = DispatchOnce() override func viewWillAppear() { super.viewWillAppear() setUpOnce.perform { // Do some work here // ... } } }

Actualización (28 de abril de 2017): OSSpinLock reemplazado por os_unfair_lock debido a advertencias de desaprobación en macOS SDK 10.12.

public final class /* struct */ DispatchOnce { private var lock = os_unfair_lock() private var isInitialized = false public /* mutating */ func perform(block: (Void) -> Void) { os_unfair_lock_lock(&lock) if !isInitialized { block() isInitialized = true } os_unfair_lock_unlock(&lock) } }


Todavía puede usarlo si agrega un encabezado de puente:

typedef dispatch_once_t mxcl_dispatch_once_t; void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);

Luego en un .m alguna parte:

void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) { dispatch_once(predicate, block); }

Ahora debería poder usar mxcl_dispatch_once de Swift.

En su mayoría, debería usar lo que Apple sugiere en su lugar, pero tuve algunos usos legítimos donde necesitaba dispatch_once con un solo token en dos funciones y no está cubierto por lo que Apple proporciona.


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. Una exploración del patrón Singleton en Swift. Todos los enfoques a continuación admiten la inicialización diferida y la seguridad de subprocesos. el enfoque dispatch_once no funciona en Swift 3.0

Enfoque A: constante de clase

class SingletonA { static let sharedInstance = SingletonA() init() { println("AAA"); } }

Enfoque B: estructura anidada

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

Enfoque C: dispatch_once

class SingletonC { class var sharedInstance: SingletonC { struct Static { static var onceToken: dispatch_once_t = 0 static var instance: SingletonC? = nil } dispatch_once(&Static.onceToken) { Static.instance = SingletonC() } return Static.instance! } }