在Swift 3.0中原有的Dispatch once已經被廢棄了,這種寫法已經不再被支援了
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
複製程式碼
文件說明:
Dispatch The free function dispatch_once is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once provided. Example:
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
複製程式碼
官方說我們可以使用懶載入初始化的全域性變數或靜態屬性,也可以得到類似 dispatch_once 提供的執行緒安全的實現方案,但是有些時候使用這種方式意義不大。
我們可以通過給DispatchQueue實現一個擴充套件方法來實現原有的功能:
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()
}
}
複製程式碼
通過使用 token 作為唯一標識 執行 once 方法時通過加鎖避免多執行緒下的 token 值不確定的情況。像這樣呼叫:
DispatchQueue.once(token: "com.me.project") {
print( "Do This Once!" )
}
複製程式碼
或者使用UUID
private let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print( "Do This Once!" )
}
複製程式碼
當然如果覺得每次使用 once 方法都要想一個不同的 token 有些傷腦可以使用下面的方式:
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()
}
複製程式碼
也支援自定義 token 的使用方式:
DispatchQueue.once(token: "com.me.project") {
setupUI()
}
複製程式碼
每次自動生成一個類似 "/Users/iOS/Demo/ViewController.swift:viewWillAppear:136"
這樣的 token 避免每次都要費盡腦汁定義一個 token,看起來也更直觀。
注意:如果在兩個模組中有同名的檔案並且使用 once 之處在同一個方法的相同行處,可能會發生衝突。這種情況雖然概率很低但也是有可能發生的,若出現此情況請定義唯一的 token,使用傳遞引數這種方式來實現。