本篇文章介紹的是在Swift3語言下的GCD應用操作,針對的目標讀者可以是沒有GCD基礎的初學者,也可以是對GCD有一定的瞭解但想更加全面的瞭解開發者。 一、GCD 介紹 背景 在 iOS 當中,蘋果提供了兩種方式進行多任務編程:Grand Central Dispatch (GCD) 和 NSOp ...
本篇文章介紹的是在Swift3語言下的GCD應用操作,針對的目標讀者可以是沒有GCD基礎的初學者,也可以是對GCD有一定的瞭解但想更加全面的瞭解開發者。
一、GCD 介紹
- 背景
在 iOS 當中,蘋果提供了兩種方式進行多任務編程:Grand Central Dispatch (GCD) 和 NSOperationQueue。當我們需要把任務分配到不同的線程中,或者是非主隊列的其它隊列中時,這兩種方法都可以很好地滿足需求。選擇哪一種方法是很主觀的行為,但是本教程只關註前一種,即 GCD。不管使用哪一種方法,有一條規則必須要牢記:任何操作都不能堵塞主線程,必須使其用於界面響應以及用戶交互。所有的耗時操作或者對 CPU 需求大的任務都要在併發或者後臺隊列中執行。
GCD 是在 iOS 4 中推出的,它為併發、性能以及並行任務提供了很大的靈活性和選擇性。但是在 Swift 3 之前,它有一個很大的劣勢:由於它的編程風格很接近底層的 C,與 Swift 的編程風格差別很大, API 很難記,即使是在 Objective-C 當中使用也很不方便。這就是很多開發都避免使用 GCD 而選擇 NSOperationQueue 的主要原因。簡單地百度一下,你就能瞭解 GCD 曾經的語法是怎麼樣的。
Swift 3 中,這些都有了很大的變化。Swift 3 採用了全新的 Swift 語法風格改寫了 GCD,這讓開發都可以很輕鬆地上手。而這些變化讓我有了動力來寫這篇文章,這裡主要介紹了 Swift 3 當中 GCD 最基礎也最重要的知識。如果你曾經使用過舊語法風格的 GCD(即使只用過一點),那麼這裡介紹的新風格對你來說就是小菜一碟;如果你之前沒有使用過 GCD,那你就即將開啟一段編程的新篇章。
-
GCD 概念
- 隊列
首先,GCD 中的核心詞是 dispatch queue。一個隊列實際上就是一系列的代碼塊,這些代碼可以在主線程或後臺線程中以同步或者非同步的方式執行。一旦隊列創建完成,操作系統就接管了這個隊列,並將其分配到任意一個核心中進行處理。不管有多少個隊列,它們都能被系統正確地管理,這些都不需要開發者進行手動管理。隊列遵循 FIFO 模式(先進先出),這意味著先進隊列的任務會先被執行(想像在櫃臺前排隊的隊伍,排在第一個的會首先被服務,排在最後的就會最後被服務)。
- 任務
接下來,另一個重要的概念就是 WorkItem(任務項)。一個任務項就是一個代碼塊,它可以隨同隊列的創建一起被創建,也可以被封裝起來,然後在之後的代碼中進行復用。正如你所想,任務項的代碼就是 dispatch queue 將會執行的代碼。隊列中的任務項也是遵循 FIFO 模式。這些執行可以是同步的,也可以是非同步的。對於同步的情況下,應用會一直堵塞當前線程,直到這段代碼執行完成。而當非同步執行的時候,應用先執行任務項,不等待執行結束,立即返回。我們會在後面的實例里看到它們的區別。
- 串列、並行
瞭解完上面兩個概念(隊列和任務項)之後,我們需要知道一個隊列可以是串列或並行的。在串列隊列中,一個任務項只有在前一個任務項完成後才能執行(除非它是第一個任務項),而在並行隊列中,所有的任務項都可以並行執行。
在為主隊列添加任務時,無論何時都要加倍小心。這個隊列要隨時用於界面響應以及用戶交互。並且記住一點,所有與用戶界面相關的更新都必須在主線程執行。如果你嘗試在後臺線程更新 UI,系統並不保證這個更新何時會發生,大多數情況下,這會都用戶帶來不好的體驗。但是,所有發生在界面更新前的任務都可以在後臺線程執行。舉例來說,我們可以在從隊列,或者後臺隊列中下載圖片數據,然後在主線程中更新對應的 image view。
- 全局隊列
我們不一定需要每次都創建自己的隊列。系統維護的全局隊列可以用來執行任何我們想執行的任務。至於隊列在哪一個線程運行,iOS 維護了一個線程池,即一系列除主線程之外的線程,系統會從中挑選一至多條線程來使用(取決於你所創建的隊列的數據,以及隊列創建的方式)。哪一條線程會被使用,對於開發者來說是未知的,而是由系統根據當前的併發任務,處理器的負載等情況來進行“決定”。講真,除了系統,誰又想去處理上述的這些工作呢。
-
初步認識
- 創建一個個隊列
// 創建了一個標簽是”queue0“、執行優先順序為.default的並行隊列 let queue0 = DispatchQueue(label: "queue0", qos: .default, attributes: .concurrent) // 屬性解釋: // label:隊列標簽 // qos:設置隊列的優先順序 // attributes:隊列形式:預設串列,設置為.concurrent代表是並行隊列 // 這個初始化方法中還有兩個參數,但我們極少使用這裡就不多介紹 創建一個簡單的串列隊列(不設置優先順序) let queue1 = DispatchQueue(label: "queue1")
- 創建一個有優先順序的串列隊列
let queue2 = DispatchQueue(label: "queue2", qos: .background)
- 創建一個並行隊列
// 未設優先順序 let queue3 = DispatchQueue(label: "queue3", attributes: .concurrent) // 設置了優先順序 let queue4 = DispatchQueue(label: "queue4", qos: .background,attributes: .concurrent)
- 系統的全局隊列
// 系統的全局隊列 let queue5 = DispatchQueue.global() let queue6 = DispatchQueue.global(qos: .default)
- 系統的主隊列
// 系統主隊列:切記這主要用來操作UI頁面相關信息,不要隨便添加耗時任務進來 let main = DispatchQueue.main
GCD 串列
- 串列同步任務
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) queueExample() } func queueExample() { // 串列隊列 let queue1 = DispatchQueue.global() // 同步任務 // 任務1 queue1.sync { for i in 1...10 { print("