Swift 3.0之后實現(xiàn)Dispatch once擴展

在Swift 3.0中原有的Dispatch once已經(jīng)被廢棄了,這種寫法已經(jīng)不再被支持了

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

官方說我們可以使用懶加載初始化的全局變量或靜態(tài)屬性,也可以得到類似 dispatch_once 提供的線程安全的實現(xiàn)方案,但是有些時候使用這種方式意義不大。

我們可以通過給DispatchQueue實現(xiàn)一個擴展方法來實現(xiàn)原有的功能:

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 作為唯一標識 執(zhí)行 once 方法時通過加鎖避免多線程下的 token 值不確定的情況。像這樣調(diào)用:

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

調(diào)用方式更簡單:

DispatchQueue.once {
    setupUI()
}

也支持自定義 token 的使用方式:

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

每次自動生成一個類似 "/Users/iOS/Demo/ViewController.swift:viewWillAppear:136"這樣的 token 避免每次都要費盡腦汁定義一個 token,看起來也更直觀。
注意:如果在兩個模塊中有同名的文件并且使用 once 之處在同一個方法的相同行處,可能會發(fā)生沖突。這種情況雖然概率很低但也是有可能發(fā)生的,若出現(xiàn)此情況請定義唯一的 token,使用傳遞參數(shù)這種方式來實現(xiàn)。

相關鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容