做者:GABRIEL THEODOROPOULOS,原文連接,原文日期:2016-11-16
譯者:小鍋;校對:saitjr;定稿:CMBhtml
自中央處理器(CPU)出現以來,最大的技術進步當屬多核處理器,這意味着它能夠同時運行多條線程,而且能夠在任什麼時候刻處理至少一個任務。git
串行執行以及僞多線程都已經成爲了歷史,若是你經歷過老式電腦的時代,又或者你接觸過搭載着舊操做系統的舊電腦,你就能輕易明白個人話。可是,無論 CPU 擁有多少個核心,無論它有多麼強大,開發者若是很差好利用這些優點 ,那就沒有任何意義。這時就須要使用到多線程以及多任務編程了。開發者不只能夠,並且必需要好好利用設備上 CPU 的多線程能力,這就須要開發者將程序分解爲多個部分,並讓它們在多個線程中併發執行。github
併發編程有不少好處,可是最明顯的優點包括用更少的時間完成所需的任務,防止界面卡頓,展示更佳的用戶體驗,等等。想像一下,若是應用須要在主線程下載一堆圖片,那種體驗有多糟糕,界面會一直卡頓直到全部的下載任務完成;用戶是絕對不接受這種應用的。編程
在 iOS 當中,蘋果提供了兩種方式進行多任務編程:Grand Central Dispatch (GCD) 和 NSOperationQueue。當咱們須要把任務分配到不一樣的線程中,或者是非主隊列的其它隊列中時,這兩種方法均可以很好地知足需求。選擇哪種方法是很主觀的行爲,可是本教程只關注前一種,即 GCD。無論使用哪種方法,有一條規則必需要牢記:任何操做都不能堵塞主線程,必須使其用於界面響應以及用戶交互。全部的耗時操做或者對 CPU 需求大的任務都要在併發或者後臺隊列中執行。對於新手來講,理解和實踐可能都會比較難,這也正是這篇文章的意義所在。swift
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 中的核心詞是 dispatch queue。一個隊列實際上就是一系列的代碼塊,這些代碼能夠在主線程或後臺線程中以同步或者異步的方式執行。一旦隊列建立完成,操做系統就接管了這個隊列,並將其分配到任意一個核心中進行處理。無論有多少個隊列,它們都能被系統正確地管理,這些都不須要開發者進行手動管理。隊列遵循 FIFO 模式(先進先出),這意味着先進隊列的任務會先被執行(想像在櫃檯前排隊的隊伍,排在第一個的會首先被服務,排在最後的就會最後被服務)。咱們會在後面的第一個例子中更清楚地理解這個概念。閉包
接下來,另外一個重要的概念就是 WorkItem(任務項)。一個任務項就是一個代碼塊,它能夠隨同隊列的建立一塊兒被建立,也能夠被封裝起來,而後在以後的代碼中進行復用。正如你所想,任務項的代碼就是 dispatch queue 將會執行的代碼。隊列中的任務項也是遵循 FIFO 模式。這些執行能夠是同步的,也能夠是異步的。對於同步的狀況下,應用會一直堵塞當前線程,直到這段代碼執行完成。而當異步執行的時候,應用先執行任務項,不等待執行結束,當即返回。咱們會在後面的實例裏看到它們的區別。併發
瞭解完這兩個概念(隊列和任務項)以後,咱們須要知道一個隊列能夠是串行或並行的。在串行隊列中,一個任務項只有在前一個任務項完成後才能執行(除非它是第一個任務項),而在並行隊列中,全部的任務項均可以並行執行。app
在爲主隊列添加任務時,不管什麼時候都要加倍當心。這個隊列要隨時用於界面響應以及用戶交互。而且記住一點,全部與用戶界面相關的更新都必須在主線程執行。若是你嘗試在後臺線程更新 UI,系統並不保證這個更新什麼時候會發生,大多數狀況下,這會都用戶帶來很差的體驗。可是,全部發生在界面更新前的任務均可以在後臺線程執行。舉例來講,咱們能夠在從隊列,或者後臺隊列中下載圖片數據,而後在主線程中更新對應的 image view。
咱們不必定須要每次都建立本身的隊列。系統維護的全局隊列能夠用來執行任何咱們想執行的任務。至於隊列在哪個線程運行,iOS 維護了一個線程池,即一系列除主線程以外的線程,系統會從中挑選一至多條線程來使用(取決於你所建立的隊列的數據,以及隊列建立的方式)。哪一條線程會被使用,對於開發者來講是未知的,而是由系統根據當前的併發任務,處理器的負載等狀況來進行「決定」。講真,除了系統,誰又想去處理上述的這些工做呢。
在本文中,接下來咱們會使用幾個小的,具體的示例來介紹 GCD 的概念。正常狀況下,咱們使用 Playground 來演示就能夠了,並不須要建立一個 demo 應用,可是咱們沒辦法使用 Playground 來演示 GCD 的示例。由於在 Playground 當中沒法使用不一樣的線程來調用函數,儘管咱們的一些示例是能夠在上面運行的,但並非所有。所以,咱們使用一個正常的工程來進行演示,以克服全部可能碰到的潛在問題,你能夠在這裏下載項目並打開。
這個工程幾乎是空的,除了下述額外的兩點:
在 ViewController.swift
文件中,咱們能夠看到一系列未實現的方法。每個方法中,咱們都將演示一個 GCD 的特性,你要作的事情就是在在 viewDidAppear(_:)
中去除相應方法調用的註釋,讓對應的方法被調用 。
在 Main.storyboard
中,ViewController
控制器添加了一個 imageView
,而且它的 IBOutlet 屬性已經被正確地鏈接到 ViewController
類當中。稍後咱們將會使用這個 imageView
來演示一個真實的案例。
如今讓咱們開始吧。
在 Swift 3 當中,建立一個 dispatch queue 的最簡單方式以下:
let queue = DispatchQueue(label: "com.appcoda.myqueue")
你惟一要作的事就是爲你的隊列提供一個獨一無二的標籤(label)。使用一個反向的 DNS 符號("com.appcoda.myqueue")就很好,由於用它很容易創造一個獨一無二的標籤,甚至連蘋果公司都是這樣建議的。儘管如此,這並非強制性的,你可使用你喜歡的任何字符串,只要這個字符串是惟一的。除此以外,上面的構造方法並非建立隊列的惟一方式。在初始化隊列的時候能夠提供更多的參數,咱們會在後面的篇幅中談論到它。
一旦隊列被建立後,咱們就可使用它來執行代碼了,可使用 sync
方法來進行同步執行,或者使用 async
方法來進行異步執行。由於咱們剛開始,因此先使用代碼塊(一個閉包)來做爲被執行的代碼。在後面的篇幅中,咱們會初始化並使用 dispatch 任務項(DispatchWorkItem)來取代代碼塊(須要注意的是,對於隊列來講代碼塊也算是一個任務項)。咱們先從同步執行開始,下面要作的就是打印出數字 0~9 :
使用紅點可讓咱們更容易在控制檯輸出中識別出打印的內容,特別是當咱們後面添加更多的隊列執行的時候
將上述代碼段複製粘貼到 ViewController.swift
文件中的 simpleQueues()
方法內。確保這個方法在 ViewDidAppear(_:)
裏沒有被註釋掉,而後執行。觀察 Xcode 控制檯,你會看到輸出並無什麼特別的。咱們看到控制檯輸出了一些數字,可是這些數字沒有辦法幫咱們作出關於 GCD 特性的任何結論。接下來,更新 simpleQueues()
方法內的代碼,在爲隊列添加閉包的代碼後面增長另外一段代碼。這段代碼用於輸出數字 100 ~ 109(僅用於區別數字不一樣):
for i in 100..<110 { print("Ⓜ️", i) }
上面的這個 for 循環會在主隊列運行,而第一個會在後臺線程運行。程序的運行會在隊列的 block 停止,而且直到隊列的任務結束前,它都不會執行主線程,也不會打印數字 100 ~ 109。程序會有這樣的行爲,是由於咱們使用了同步執行。你也能夠在控制檯中看到輸出結果:
可是若是咱們使用 async
方法運行代碼塊會發生什麼事呢?在這種狀況下,程序不須要等待隊列任務完成才往下執行,它會立馬返回主線程,而後第二個 for 循環會與隊列裏的循環同時運行。在咱們看到會發生什麼事以前,將隊列的執行改用 async
方法:
如今,執行代碼,並查看 Xcode 的控制檯:
對比同步執行,此次的結果有趣多了。咱們看到主隊列中的代碼(第二個 for 循環)和 dispatch queue 裏面的代碼並行運行了。在這裏,這個自定義隊列在一開始的時候得到了更多的執行時間,可是這只是跟優先級有關(這咱們將在文章後面學習到)。這裏想要強調的是,當另一個任務在後臺執行的時候,主隊列是處於空閒狀態的,隨時能夠執行別的任務,而同步執行的隊列是不會出現這種狀況的。
儘管上面的示例很簡單,但已經清楚地展現了一個程序在同步隊列與異步隊列中行爲的差別。咱們將在接下來的示例中繼續使用這種彩色的控制檯輸出,請記住,特定顏色代碼特定隊列的運行結果,不一樣的顏色表明不一樣的隊列。
在使用 GCD 與 dispatch queue 時,咱們常常須要告訴系統,應用程序中的哪些任務比較重要,須要更高的優先級去執行。固然,因爲主隊列老是用來處理 UI 以及界面的響應,因此在主線程執行的任務永遠都有最高的優先級。無論在哪一種狀況下,只要告訴系統必要的信息,iOS 就會根據你的需求安排好隊列的優先級以及它們所須要的資源(好比說所需的 CPU 執行時間)。雖然全部的任務最終都會完成,可是,重要的區別在於哪些任務更快完成,哪些任務完成得更晚。
用於指定任務重要程度以及優先級的信息,在 GCD 中被稱爲 Quality of Service(QoS)。事實上,QoS 是有幾個特定值的枚舉類型,咱們能夠根據須要的優先級,使用合適的 QoS 值來初始化隊列。若是沒有指定 QoS,則隊列會使用默認優先級進行初始化。要詳細瞭解 QoS 可用的值,能夠參考這個文檔,請確保你仔細看過這個文檔。下面的列表總結了 Qos 可用的值,它們也被稱爲 QoS classes。第一個 class 代碼了最高的優先級,最後一個表明了最低的優先級:
userInteractive
userInitiated
default
utility
background
unspecified
如今回到咱們的項目中,此次咱們要使用 queueWithQos()
方法。先聲明和初始化下面兩個 dispatch queue:
let queue1 = DispatchQueue(label: "com.appcoda.queue1", qos: DispatchQoS.userInitiated) let queue2 = DispatchQueue(label: "com.appcoda.queue2", qos: DispatchQoS.userInitiated)
注意,這裏咱們使用了相同的 QoS class,因此這兩個隊列擁有相同的運行優先級。就像咱們以前所作的同樣,第一個隊列會執行一個循環並打印出 0 ~ 9(加上前面的紅點)。第二個隊列會執行另外一個打印出 100 ~ 109 的循環(使用藍點)。
看到運行結果,咱們能夠確認這兩個隊列確實擁有相同的優先級(相同的 QoS class)—— 不要忘記在 viewDidAppear(_:)
中關閉 queueWithQos()
方法的註釋:
從上面的截圖當中能夠輕易看出這兩個任務被「均勻」地執行,而這也是咱們預期的結果。如今讓咱們把 queue2
的 QoS class 設置爲 utility
(低優先級),以下所示:
let queue2 = DispatchQueue(label: "com.appcoda.queue2", qos: DispatchQoS.utility)
如今看看會發生什麼:
毫無疑問地,第一個 dispatch queue(queue1)比第二個執行得更快,由於它的優先級比較高。即便 queue2
在第一個隊列執行的時候也得到了執行的機會,但因爲第一個隊列的優先級比較高,因此係統把多數的資源都分配給了它,只有當它結束後,系統纔會去關心第二個隊列。
如今讓咱們再作另一個試驗,此次將第一個 queue 的 QoS class 設置爲 background
:
let queue1 = DispatchQueue(label: "com.appcoda.queue1", qos: DispatchQoS.background)
這個優先級幾乎是最低的,如今運行代碼,看看會發生什麼:
此次第二個隊列完成得比較早,由於 utility
的優先級比較 background
來得高。
經過上述的例子,咱們已經清楚了 QoS 是如何運行的,可是若是咱們在同時在主隊列執行任務的話會怎麼樣呢?如今在方法的末尾加入下列的代碼:
for i in 1000..<1010 { print("Ⓜ️", i) }
同時,將第一個隊列的 QoS class 設置爲更高的優先級:
let queue1 = DispatchQueue(label: "com.appcoda.queue1", qos: DispatchQoS.userInitiated)
下面是運行結果:
咱們再次看到了主隊列默認擁有更高的優先級,queue1
與主列隊是並行執行的。而 queue2
是最後完成的,而且妝其它兩個隊列在執行的時候,它沒有獲得太多執行的機會,由於它的優先級是最低的。
到目前爲止,咱們已經看到了 dispatch queue 分別在同步與異步下的運行狀況,以及操做系統如何根據 QoS class 來影響隊列的優先級的。可是在前面的例子當中,咱們都是將隊列設置爲串行(serial)的。這意味着,若是咱們向隊列中加入超過一個的任務,這些任務將會被一個接一個地依次執行,而非同時執行。接下來,咱們將學習如何使多個任務同時執行,換句話說,咱們將學習如何使用並行(concurrent)隊列。
在項目中,此次咱們會使用 concurrentQueue()
方法(請在 viewDidAppear(_:)
方法中將對應的代碼取消註釋)。在這個新方法中,建立以下的新隊列:
let anotherQueue = DispatchQueue(label: "com.appcoda.anotherQueue", qos: .utility)
如今,將以下的任務(或者對應的任務項)添加到隊列中:
當這段代碼執行的時候,這些任務會被以串行的方式執行。這能夠在下面的截圖上看得很清楚:
接下來,咱們修改下 anotherQueue
隊列的初始化方式:
let anotherQueue = DispatchQueue(label: "com.appcoda.anotherQueue", qos: .utility, attributes: .concurrent)
在上面的初始化當中,有一個新的參數:attributes
。當這個參數被指定爲 concurrent
時,該特定隊列中的全部任務都會被同時執行。若是沒有指定這個參數,則隊列會被設置爲串行隊列。事實上,QoS 參數也不是必須的,在上面的初始化中,即便咱們將這些參數去掉也不會有任何問題。
如今從新運行代碼,能夠看到任務都被並行地執行了:
注意,改變 QoS class 也會影響程序的運行。可是,只要在初始化隊列的時候指定了 concurrent
,這些任務就會以並行的方式運行,而且它們各自都會擁有運行時間。
這個 attributes
參數也能夠接受另外一個名爲 initiallyInactive
的值。若是使用這個值,任務不會被自動執行,而是須要開發者手動去觸發。咱們接下來會進行說明,可是在這以前,須要對代碼進行一些改動。首先,聲明一個名爲 inactiveQueue
的成員屬性,以下所示:
var inactiveQueue: DispatchQueue!
如今,初始化隊列,並將其賦值給 inactiveQueue
:
let anotherQueue = DispatchQueue(label: "com.appcoda.anotherQueue", qos: .utility, attributes: .initiallyInactive) inactiveQueue = anotherQueue
使用成員屬性是有必要的,由於 anotherQueue
是在 concurrentQueues()
方法中定義的,只在該方法中可用。當它退出這個方法的時候,應用程序將沒法使用這個變量,咱們也沒法激活這個隊列,最重要的是,可能會形成運行時崩潰。
如今從新運行程序,能夠看到控制檯沒有任何的輸出,這正是咱們預期的。如今能夠在 viewDidAppear(_:)
方法中添加以下的代碼:
if let queue = inactiveQueue { queue.activate() }
DispatchQueue
類的 activate()
方法會讓任務開始執行。注意,這個隊列並無被指定爲並行隊列,所以它們會以串行的方式執行:
如今的問題是,咱們如何在指定 initiallyInactive
的同時將隊列指定爲並行隊列?其實很簡單,咱們能夠將兩個值放入一個數組當中,做爲 attributes
的參數,替代本來指定的單一數值:
let anotherQueue = DispatchQueue(label: "com.appcoda.anotherQueue", qos: .userInitiated, attributes: [.concurrent, .initiallyInactive])
有時候,程序須要對代碼塊裏面的任務項進行延時操做。GCD 容許開發者經過調用一個方法來指定某個任務在延遲特定的時間後再執行。
此次咱們將代碼寫在 queueWithDelay()
方法內,這個方法也在初始項目中定義好了。咱們會從添加以下代碼開始:
let delayQueue = DispatchQueue(label: "com.appcoda.delayqueue", qos: .userInitiated) print(Date()) let additionalTime: DispatchTimeInterval = .seconds(2)
一開始,咱們像一般同樣建立了一個 DispatchQueue
,這個隊列會在下一步中被使用到。接着,咱們打印了當前時間,以後這個時間將會被用來驗證執行任務的延遲時間,最後咱們指定了延遲時間。延遲時間一般是一個 DispatchTimeInterval
類型的枚舉值(在內部它被表示爲整型值),這個值會被添加到 DispatchTime
中用於指定延遲時間。在這個示例中,設定的等待執行時間是兩秒。這裏咱們使用的是 seconds
方法,除此以外,還有如下的方法可使用:
microseconds
milliseconds
nanoseconds
如今開始使用這個隊列:
delayQueue.asyncAfter(deadline: .now() + additionalTime) { print(Date()) }
now()
方法返回當前的時間,而後咱們額外把須要延遲的時間添加進來。如今運行程序,控制檯將會打印出以下的輸出:
的確,dispatch queue 中的任務在兩秒後被執行了。除此以外,咱們還有別的方法能夠用來指定執行時間。若是不想使用任務預約義的方法,你能夠直接使用一個 Double
類型的值添加到當前時間上:
delayQueue.asyncAfter(deadline: .now() + 0.75) { print(Date()) }
在這個狀況下,任務會被延遲 0.75 秒後執行。也能夠不使用 now()
方法,這樣一來,咱們就必須手動指定一個值做爲 DispatchTime
的參數。上面演示的只是一個延遲執行的最簡單方法,但實際上你也不大須要別的方法了。
在前面的全部例子當中,咱們都手動建立了要使用的 dispatch queue。實際上,咱們並不老是須要本身手動建立,特別是當咱們不須要改變隊列的優先級的時候。就像我在文章一開頭講過的,操做系統會建立一個後臺隊列的集合,也被稱爲全局隊列(global queue)。你能夠像使用本身建立的隊列同樣來使用它們,只是要注意不能濫用。
訪問全局隊列十分簡單:
let globalQueue = DispatchQueue.global()
能夠像咱們以前使用過的隊列同樣來使用它:
當使用全局隊列的時候,並無太多的屬性可供咱們進行修改。可是,你仍然能夠指定你想要使用隊列的 Quality of Service:
let globalQueue = DispatchQueue.global(qos: .userInitiated)
若是沒有指定 QoS class(就像本節的第一個示例),就會默認以 default
做爲默認值。
不管你使不使用全局隊列,你都不可避免地要常常訪問主隊列,大多數狀況下是做爲更新 UI 而使用。在其它隊列中訪問主隊列的方法也很是簡單,就以下面的代碼片斷所示,而且須要在調用的同時指定同步仍是異步執行:
DispatchQueue.main.async { // Do something }
事實上,你能夠輸入 DispatchQueue.main.
來查看主隊列的全部可用選項,Xcode 會經過自動補全來顯示主隊列全部可用的方法,不過上面代碼展現的就是咱們絕大多數時間會用到的(事實上,這個方法是通用的,對於全部隊列,均可以經過輸入 . 以後讓 Xcode 來進行自動補全)。就像上一節所作的同樣,你也能夠爲代碼的執行增長延時。
如今讓咱們來看一個真實的案例,演示如何經過主隊列來更新 UI。在初始工程的 Main.storyboard
文件中有一個 ViewController
場景(sence),這個 ViewController
場景包含了一個 imageView
,而且這個 imageView 已經經過 IBOutlet
鏈接到對應的 ViewController
類文件中。在這裏,咱們經過 fetchImage()
方法(目前是空的)來下載一個 Appcoda 的 logo 並將其展現到 imageView
當中。下面的代碼完成了上述動做(我不會在這裏針對 URLSession 作相關的討論,以及介紹它如何使用):
func fetchImage() { let imageURL: URL = URL(string: "http://www.appcoda.com/wp-content/uploads/2015/12/blog-logo-dark-400.png")! (URLSession(configuration: URLSessionConfiguration.default)).dataTask(with: imageURL, completionHandler: { (imageData, response, error) in if let data = imageData { print("Did download image data") self.imageView.image = UIImage(data: data) } }).resume() }
注意,咱們並無在主隊列更新 UI 界面,而是試圖在 dataTask(...)
方法的 completion handler 裏運行的後臺線程來更新界面。編譯、運行程序,看看會發生什麼(不要忘記調用 fetchImage()
方法):
即便咱們獲得了圖片下載完成的信息,可是沒有看到圖片被顯示到 imageView
上面,這是由於 UI 並無更新。大多數狀況下,這個圖片會在信息出現的一小會後顯示出來(可是若是其餘任務也在應用程序中執行,上述狀況不保證會發生),不只如此,你還會在控制檯看到關於在後臺線程更新 UI 的一大串出錯信息。
如今,讓咱們改正這段有問題的行爲,使用主隊列來更新用戶界面。在編輯上述方法的時候,只須要改動底下所示部分,並注意咱們是如何使用主隊列的:
if let data = imageData { print("Did download image data") DispatchQueue.main.async { self.imageView.image = UIImage(data: data) } }
再次運行程序,會看到圖片在下載完成後被正確地顯示出來。主隊列確實被調用並更新了 UI。
DispatchWorkItem
是一個代碼塊,它能夠在任意一個隊列上被調用,所以它裏面的代碼能夠在後臺運行,也能夠在主線程運行。它的使用真的很簡單,就是一堆能夠直接調用的代碼,而不用像以前同樣每次都寫一個代碼塊。
下面展現了使用任務項最簡單的方法:
let workItem = DispatchWorkItem { // Do something }
如今讓咱們經過一個小例子來看看 DispatchWorkItem
如何使用。前往 useWorkItem()
方法,並添加以下代碼:
func useWorkItem() { var value = 10 let workItem = DispatchWorkItem { value += 5 } }
這個任務項的目的是將變量 value
的值增長 5。咱們使用任務項對象去調用 perform()
方法,以下所示:
workItem.perform()
這行代碼會在主線程上面調用任務項,可是你也可使用其它隊列來執行它。參考下面的示例:
let queue = DispatchQueue.global() queue.async { workItem.perform() }
這段代碼也能夠正常運行。可是,有一個更快地方法能夠達到一樣的效果。DispatchQueue
類爲此目的提供了一個便利的方法:
queue.async(execute: workItem)
當一個任務項被調用後,你能夠通知主隊列(或者任何其它你想要的隊列),以下所示:
workItem.notify(queue: DispatchQueue.main) { print("value = ", value) }
上面的代碼會在控制檯打印出 value
變量的值,而且它是在任務項被執行的時候打印的。如今將全部代碼放到一塊兒,userWorkItem()
方法內的代碼以下所示:
func useWorkItem() { var value = 10 let workItem = DispatchWorkItem { value += 5 } workItem.perform() let queue = DispatchQueue.global(qos: .utility) queue.async(execute: workItem) workItem.notify(queue: DispatchQueue.main) { print("value = ", value) } }
下面是你運行程序後會看到的輸出(記得在 viewDidAppear(_:)
方法中調用上面的方法):
這篇文章中提到的知識足夠你應付大多數狀況下的多任務和併發編程了。可是,請記住,還有其它咱們沒有提到的 GCD 概念,或者文章有提到可是沒有深刻討論的概念。目的是想讓本篇文章對全部層次的開發者都簡單易讀。若是你以前沒有使用過 GCD,請認真考慮並嘗試一下,讓主隊列從繁重的任務中解脫出來。若是有能夠在後臺線程執行的任務,讓將其移到後臺運行。在任何狀況下,使用 GCD 都不困難,而且它能得到的正面結果就是讓應用響應更快。開始享受 GCD 的樂趣吧!
能夠在這個 Github 裏找到本文使用的完整項目。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。