¿Para qué sirve la nueva sintaxis 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) {
}
}
¿Para qué sirve la nueva sintaxis 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) {
}
}
pod 'SwiftDispatchOnce', '~> 1.0'
Cheers. :]Respuestas:
Del doc :
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.
fuente
dispatch_once
estaba claro. Esto, desafortunadamente, es feo y confuso ..Si bien el uso de globales inicializados diferidos 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 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!" ) }
Dado que 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() } }
fuente
objc_sync_enter
yobjc_sync_exit
más.Ampliando la respuesta de Tod Cunningham anterior, agregué otro método que crea el token automáticamente desde el archivo, la función y la 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 producirse una colisión si tiene el mismo archivo en dos módulos. Lástima que no haya
#module
fuente
Editar
La respuesta de @ Frizlab: no se garantiza que esta solución sea segura para subprocesos. Se debe utilizar 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 }()
usado como
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() _ = dispatchOnce }
fuente
Aún puede usarlo si agrega un encabezado de puente:
Luego en
.m
algún lugar:Ahora debería poder utilizar
mxcl_dispatch_once
Swift.En su mayoría, debería usar lo que Apple sugiere, pero tuve algunos usos legítimos en los que necesitaba
dispatch_once
con un solo token en dos funciones y no está cubierto por lo que Apple proporciona.fuente
Puede declarar una función variable de nivel superior como esta:
private var doOnce: ()->() = { /* do some work only once per instance */ return {} }()
luego llame a esto en cualquier lugar:
fuente
Swift 3: para aquellos a los que 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
reemplazada conos_unfair_lock
advertencias de obsolescencia debida 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) } }
fuente
OSSpinLock
reemplazado conos_unfair_lock
. Por cierto: Aquí hay un buen video de la WWDC sobreConcurrent Programming
: developer.apple.com/videos/play/wwdc2016/720Mejoro las respuestas anteriores obtengo resultado:
import Foundation extension DispatchQueue { private static var _onceTracker = [AnyHashable]() ///only excute once in same file&&func&&line public class func onceInLocation(file: String = #file, function: String = #function, line: Int = #line, block: () -> Void) { let token = "\(file):\(function):\(line)" once(token: token, block: block) } ///only excute once in same Variable public class func onceInVariable(variable:NSObject, block: () -> Void){ once(token: variable.rawPointer, 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: AnyHashable,block: () -> Void) { objc_sync_enter(self) defer { objc_sync_exit(self) } guard !_onceTracker.contains(token) else { return } _onceTracker.append(token) block() } } extension NSObject { public var rawPointer:UnsafeMutableRawPointer? { get { Unmanaged.passUnretained(self).toOpaque() } } }
fuente
Use el enfoque de constante de clase 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 siguientes admiten la inicialización diferida y la seguridad de subprocesos. El enfoque dispatch_once no funciona en Swift 3.0
Método A: constante de clase
class SingletonA { static let sharedInstance = SingletonA() init() { println("AAA"); } }
Método 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! } }
fuente