Swift 3必看:從使用場景瞭解GCD新API

沒故事的卓同學發表於2016-10-06

swift 3中對C層級的GCD的API進行了徹頭徹尾的改變。本文將從實際使用場景來了解一下新的api使用。

dispatch_async

一個常見的場景就是在一個全域性佇列進行一些操作後切換到主執行緒配置UI。現在是這麼寫:

DispatchQueue.global().async {
    // code
    DispatchQueue.main.async {
        // 主執行緒中
    }
}複製程式碼

global()是一個有著預設引數的靜態函式:

class DispatchQueue : DispatchObject {
    public  class var main: DispatchQueue 
    public class func global(qos: DispatchQoS.QoSClass = default) -> DispatchQueue
}複製程式碼

sync

如果想同步執行操作,和async類似,呼叫sync就可以了:

DispatchQueue.global().sync {
    // 同步執行
}複製程式碼

優先順序:DispatchQoS

我們知道,GCD 的預設佇列優先順序有四個:

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_DEFAULT
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND

現在則改為了QoSClass列舉

 public enum QoSClass {

        case background

        case utility

        case `default`

        case userInitiated

        case userInteractive

        case unspecified

        public init?(rawValue: qos_class_t)

        public var rawValue: qos_class_t { get }
    }複製程式碼

這些命名比原先的更加友好,能更好表達這個操作的意圖。

和原有的對應關係是:

* DISPATCH_QUEUE_PRIORITY_HIGH:         .userInitiated
* DISPATCH_QUEUE_PRIORITY_DEFAULT:      .default
* DISPATCH_QUEUE_PRIORITY_LOW:          .utility
* DISPATCH_QUEUE_PRIORITY_BACKGROUND:   .background複製程式碼

建立佇列

DispatchQueue的預設初始化方法建立的就是一個同步佇列,如果要建立併發的佇列,在attributes中宣告concurrent。

// 同步佇列
let serialQueue = DispatchQueue(label: "queuename")

// 併發佇列
let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)複製程式碼

推遲時間後執行

原先的dispatch_time_t現在由DispatchTime物件表示。可以用靜態方法now獲得當前時間,然後再通過加上一個DispatchTimeInterval列舉來獲得一個需要延遲的時間。

let delay = DispatchTime.now() + DispatchTimeInterval.seconds(60)
DispatchQueue.main.asyncAfter(deadline: delay) { 
    // 延遲執行
}複製程式碼

這裡也可以直接加上一個秒數。

let three = DispatchTime.now() + 3.0複製程式碼

因為DispatchTime中自定義了+號。


public func +(time: DispatchTime, seconds: Double) -> DispatchTime複製程式碼

DispatchGroup

如果想在dispatch_queue中所有的任務執行完成後再做某種操作可以使用DispatchGroup。原先的dispatch_group_t由現在的DispatchGroup物件代替。

let group = DispatchGroup()

let queueBook = DispatchQueue(label: "book")
queueBook.async(group: group) {
    // 下載圖書
}
let queueVideo = DispatchQueue(label: "video")
queueVideo.async(group: group) {
    // 下載視訊
}

group.notify(queue: DispatchQueue.main) { 
    // 下載完成
}複製程式碼

DispatchGroup會在組裡的操作都完成後執行notify。 如果有多個併發佇列在一個組裡,我們想在這些操作執行完了再繼續,呼叫wait

group.wait()複製程式碼

DispatchWorkItem

使用DispatchWorkItem代替原來的dispatch_block_t。 在DispatchQueue執行操作除了直接傳了一個() -> Void型別的閉包外,還可以傳入一個DispatchWorkItem。


    public func sync(execute workItem: DispatchWorkItem)

    public func async(execute workItem: DispatchWorkItem)複製程式碼

DispatchWorkItem的初始化方法可以配置Qos和DispatchWorkItemFlags,但是這兩個引數都有預設引數,所以也可以只傳入一個閉包。

    public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> ())

let workItem = DispatchWorkItem { 
    // TODO:
}複製程式碼

DispatchWorkItemFlags列舉中assignCurrentContext表示QoS根據建立時的context決定。 值得一提的是DispatchWorkItem也有wait方法,使用方式和group一樣。呼叫會等待這個workItem執行完。

let myQueue = DispatchQueue(label: "my.queue", attributes: .concurrent)
let workItem = DispatchWorkItem {
    sleep(1)
    print("done")
}
myQueue.async(execute: workItem)
print("before waiting")
workItem.wait()
print("after waiting")複製程式碼

barrier

假設我們有一個併發的佇列用來讀寫一個資料物件。如果這個佇列裡的操作是讀的,那麼可以多個同時進行。如果有寫的操作,則必須保證在執行寫入操作時,不會有讀取操作在執行,必須等待寫入完成後才能讀取,否則就可能會出現讀到的資料不對。在之前我們用dipatch_barrier實現。 現在屬性放在了DispatchWorkItemFlags裡。

let wirte = DispatchWorkItem(flags: .barrier) { 
    // write data
}
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
dataQueue.async(execute: wirte)複製程式碼

訊號量

為了執行緒安全的統計數量,我們會使用訊號量作計數。原來的dispatch_semaphore_t現在用DispatchSemaphore物件表示。 初始化方法只有一個,傳入一個Int型別的數。


let semaphore = DispatchSemaphore(value: 5)

 // 訊號量減一
semaphore.wait()

 //訊號量加一
semaphore.signal()複製程式碼

dispatch_once被廢棄

在swift 3中已經被廢棄了。 簡單的建議就是一些初始化場景就用懶載入吧。

// Examples of dispatch_once replacements with global or static constants and variables. 
// In all three, the initialiser is called only once. 

// Static properties (useful for singletons).
class Object {
    static let sharedInstance = Object()
}

// Global constant.
let constant = Object()

// Global variable.
var variable: Object = {
    let variable = Object()
    variable.doSomething()
    return variable
}()複製程式碼

歡迎關注我的微博:@沒故事的卓同學

相關連結:

SE0088-Modernize libdispatch for Swift 3 naming conventions

Concurrent Programming With GCD in Swift 3

相關文章