在語言版本3中進行更改後,Swift中dispatch_once
的新語法是什麼?舊版本如下。Swatch 3 GCD API更改後的dispatch_once
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
這些製成的are the changes to libdispatch。
在語言版本3中進行更改後,Swift中dispatch_once
的新語法是什麼?舊版本如下。Swatch 3 GCD API更改後的dispatch_once
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
這些製成的are the changes to libdispatch。
從doc:
調度
自由功能dispatch_once不再提供 斯威夫特。在Swift中,您可以使用懶惰初始化的全局變量或靜態屬性,並獲得與提供的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.
這不像你不知道Swift會快速變化,你將不得不糾正Swift版本之間的許多破碎的代碼。 – Abizern
最大的痛苦就是第三方的Pod並不總是與Swift3兼容。 – Tinkerbell
這就是您引入第三方依賴關係時引發的技術債務@Tinkerbell。我喜歡Swift,但是爲了這個原因,我們特別謹慎地引入了使用它的外部依賴。 –
在使用延遲初始化的全局變量可以使一些一次性初始化意義上說,它並不適用於其他類型的意義。對於像singleton這樣的事情,使用懶惰的初始化全局變量是很有意義的,但對於守護swizzle設置等事情來說,這並沒有太多意義。
這裏是一個斯威夫特3風格實現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()
}
}
下面是一個例子用法:
DispatchQueue.once(token: "com.vectorform.test") {
print("Do This Once!")
}
或使用UUID
private let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print("Do This Once!")
}
正如我們目前在從swift 2到3的轉換時間,這裏是一個示例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()
}
}
在上面的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
你仍然可以使用它,如果你添加一個橋接報:在某處.m
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
。
大多數情況下,您應該使用蘋果的建議,但我有一些合法的用途,我需要在兩個函數中使用單個令牌來代替dispatch_once
,並且Apple沒有提供什麼內容。
如果您使用的是Swift 1.2或更高版本,並且需要支持早期版本的嵌套結構方法,請使用類常量方法。 Swift中單例模式的探索。下面的所有方法都支持延遲初始化和線程安全。類恆定 類SingletonA {
static let sharedInstance = SingletonA()
init() {
println("AAA");
}
} 方法B::嵌套結構 類SingletonB {
class var sharedInstance: SingletonB {
struct Static {
static let instance: SingletonB = SingletonB()
}
return Static.instance
}
} 方法 dispatch_once方法不是在迅速3.0
視角下的工作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!
}
}
具體詢問Swift 3解決方案的問題。 – thesummersign
斯威夫特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
// ...
}
}
}
更新(28 2017年四月):OSSpinLock
替換爲os_unfair_lock
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)
}
}
我收到OSSSpinLock在iOS 10.0中棄用的消息 – markhorrocks
謝謝!示例代碼已更新。用'os_unfair_lock'替換'OSSpinLock'。 BTW:這是一個關於'Concurrent Programming'的WWDC視頻:https://developer.apple.com/videos/play/wwdc2016/720/ – Vlad
簡單的解決方案是使用像
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
_ = dispatchOnce
}
這完全沒有幫助,因爲懶惰的var聲明不能內聯使用常規代碼,它必須位於結構或類定義中。這意味着dispatchOnce的內容無法捕獲實例的周圍範圍。例如,如果聲明一個尚未運行的閉包,則不能在該閉包中聲明該結構,並將該懶惰的var的內容作爲另一個閉包,以捕獲周圍閉包中的變量... – CommaToast
Downvoted,因爲此代碼肯定是**不是與dispatch_once相同的語義。 dispatch_once確保代碼只運行一次,**從**調用它的任何線程。懶惰變量在多線程環境中具有未定義的行爲。 – Frizlab
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()
}
}
lazy var dispatchOnce : Void = { // or anyName I choose
self.title = "Hello Lazy Guy"
return
}()
,並使用如下
executeOnce {
print("onces")
}
可能重複:[DISP何去何從atch_once in Swift 3?](http://stackoverflow.com/q/37801407/957768) – rickster
根據回答https://stackoverflow.com/a/38311178/1648724和https://stackoverflow.com/a/ 39983813/1648724,我創建了一個CocoaPod來執行此操作:['pod'SwiftDispatchOnce','〜> 1.0''](https://github.com/JRG-Developer/SwiftDispatchOnce)乾杯。 :] –