Swift 3 GCD API更改后的dispatch_once

语言版本3所做的更改之后,Swift中dispatch_once的新语法是什么? 旧版本如下。

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

这些是对libdispatch所做的更改 。

从文档 :

调度
免费函数dispatch_once在Swift中不再可用。 在Swift中,您可以使用lazily初始化的全局variables或静态属性,并获得与dispatch_once提供的相同的线程安全性和调用一次性保证。 例:

 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. 

虽然使用延迟初始化全局variables对于一次初始化可能是有意义的,但对于其他types则没有意义。 对于像singleton这样的事情,使用懒惰的初始化全局variables是很有意义的,但对于守护swizzle设置等事情来说,这并没有什么意义。

这是一个dispatch_once的Swift 3样式实现:

 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() } } 

以下是一个示例用法:

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

或使用UUID

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

由于我们目前正处于从2快速到3的过渡时期,下面是一个快速实施的例子:

 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() } } 

在上面的Tod Cunningham的回答中,我添加了另一种从文件,函数和行中自动生成令牌的方法。

 public extension DispatchQueue { private static var _onceTracker = [String]() public class func once(file: String = #file, function: String = #function, line: Int = #line, block:(Void)->Void) { let token = file + ":" + function + ":" + String(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)->Void) { objc_sync_enter(self) defer { objc_sync_exit(self) } if _onceTracker.contains(token) { return } _onceTracker.append(token) block() } } 

所以调用:

 DispatchQueue.once { setupUI() } 

如果你愿意,你仍然可以指定一个令牌:

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

如果你在两个模块中有相同的文件,我想你可能会碰到。 太糟糕了没有#module

简单的解决scheme是

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

用过

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

如果添加桥接头,仍然可以使用它:

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

然后在某个地方:

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

您现在应该能够使用Swift中的mxcl_dispatch_once

大多数情况下,你应该使用苹果公司的build议,但是我有一些合法的用途,需要在两个函数中使用单个标记来dispatch_once ,而不是Apple提供的内容。

Swift 3:对于那些喜欢可重用类(或结构)的人:

 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) } } 

用法:

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

更新 (2017年4月28日):在macOS SDK 10.12中,由于os_unfair_lock因弃用警告而被OSSpinLock所取代。

 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) } } 

如果您使用Swift 1.2或更高版本,请使用类常量方法,如果需要支持早期版本,请使用嵌套结构方法。 Swift中单例模式的探索。 下面的所有方法都支持延迟初始化和线程安全。 dispatch_once方法在swift 3.0中不起作用

方法一:类常量类SingletonA {

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

方法B:嵌套结构类SingletonB {

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

}方法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! } 

}

  I have created below function func executeOnce(code: @escaping () -> Void) { if UserDefaults.standard.value(forKey: "3333##112233") == nil { code() UserDefaults.standard.setValue("vv", forKey: "3333##112233") UserDefaults.standard.synchronize() } } 

并使用如下

  executeOnce { print("onces") }