-GCD
中的核心詞是dispatch queue
。一個隊列實際上就是一系列的代碼塊巍实,這些代碼可以在主線程或后臺線程中以同步或者異步的方式執(zhí)行画切。一旦隊列創(chuàng)建完成,操作系統(tǒng)就接管了這個隊列撩穿,并將其分配到任意一個核心中進行處理。不管有多少個隊列谒撼,它們都能被系統(tǒng)正確地管理食寡,這些都不需要開發(fā)者進行手動管理。隊列遵循 FIFO 模式(先進先出)廓潜,這意味著先進隊列的任務(wù)會先被執(zhí)行(想像在柜臺前排隊的隊伍抵皱,排在第一個的會首先被服務(wù),排在最后的就會最后被服務(wù))辩蛋。
- 另一個重要的概念就是
WorkItem
(任務(wù)項)叨叙。一個任務(wù)項就是一個代碼塊,它可以隨同隊列的創(chuàng)建一起被創(chuàng)建堪澎,也可以被封裝起來擂错,然后在之后的代碼中進行復(fù)用。正如你所想樱蛤,任務(wù)項的代碼就是dispatch queue
將會執(zhí)行的代碼钮呀。隊列中的任務(wù)項也是遵循 FIFO 模式。這些執(zhí)行可以是同步的昨凡,也可以是異步的爽醋。對于同步的情況下,應(yīng)用會一直堵塞當前線程便脊,直到這段代碼執(zhí)行完成蚂四。而當異步執(zhí)行的時候,應(yīng)用先執(zhí)行任務(wù)項哪痰,不等待執(zhí)行結(jié)束遂赠,立即返回。 - 在為主隊列添加任務(wù)時晌杰,無論何時都要加倍小心跷睦。這個隊列要隨時用于界面響應(yīng)以及用戶交互。并且記住一點肋演,所有與用戶界面相關(guān)的更新都必須在主線程執(zhí)行抑诸。如果你嘗試在后臺線程更新 UI烂琴,系統(tǒng)并不保證這個更新何時會發(fā)生,大多數(shù)情況下蜕乡,這會都用戶帶來不好的體驗奸绷。但是,所有發(fā)生在界面更新前的任務(wù)都可以在后臺線程執(zhí)行层玲。
- 我們不一定需要每次都創(chuàng)建自己的隊列号醉。系統(tǒng)維護的全局隊列可以用來執(zhí)行任何我們想執(zhí)行的任務(wù)。至于隊列在哪一個線程運行称簿,iOS 維護了一個線程池,即一系列除主線程之外的線程惰帽,系統(tǒng)會從中挑選一至多條線程來使用(取決于你所創(chuàng)建的隊列的數(shù)據(jù)憨降,以及隊列創(chuàng)建的方式)。哪一條線程會被使用该酗,對于開發(fā)者來說是未知的授药,而是由系統(tǒng)根據(jù)當前的并發(fā)任務(wù),處理器的負載等情況來進行“決定”呜魄。
Dispatch Queue
在 swift 中 dispatch queue
對應(yīng)的類為DispatchQueue
悔叽,可以使用下面方法進行初始化
let queue = (label: String,
qos: DispatchQoS = default,
attributes: DispatchQueue.Attributes = default,
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default,
target: DispatchQueue? = default)
在 Swift 3 當中,創(chuàng)建一個dispatch queue
的最簡單方式:
let queue = DispatchQueue(label: "com.appcode.myqueue")
queue.sync {
print("同步執(zhí)行")
}
queue.async {
print("異步執(zhí)行")
}
Quality Of Service (QOS) 和優(yōu)先級
在使用
GCD
與dispatch queue
時爵嗅,我們經(jīng)常需要告訴系統(tǒng)娇澎,應(yīng)用程序中的哪些任務(wù)比較重要,需要更高的優(yōu)先級去執(zhí)行睹晒。當然趟庄,由于主隊列總是用來處理 UI 以及界面的響應(yīng),所以在主線程執(zhí)行的任務(wù)永遠都有最高的優(yōu)先級伪很。不管在哪種情況下戚啥,只要告訴系統(tǒng)必要的信息,iOS 就會根據(jù)你的需求安排好隊列的優(yōu)先級以及它們所需要的資源(比如說所需的 CPU 執(zhí)行時間)锉试。雖然所有的任務(wù)最終都會完成猫十,但是,重要的區(qū)別在于哪些任務(wù)更快完成呆盖,哪些任務(wù)完成得更晚拖云。用于指定任務(wù)重要程度以及優(yōu)先級的信息,在 GCD 中被稱為
Quality of Service (Qos)
应又。事實上江兢,Qos 是有幾個特定值的枚舉類型,我們可以根據(jù)需要的優(yōu)先級丁频,使用合適的 Qos 值來初始化隊列杉允。如果沒有指定 Qos邑贴,則隊列會使用默認優(yōu)先級進行初始化。
/// 由低到高 優(yōu)先級
public static let unspecified: DispatchQoS
public static let background: DispatchQoS
public static let utility: DispatchQoS
public static let `default`: DispatchQoS
public static let userInitiated: DispatchQoS
public static let userInteractive: DispatchQoS
并發(fā)隊列
///public static let concurrent: DispatchQueue.Attributes
///public static let initiallyInactive: DispatchQueue.Attributes
///attributes 參數(shù)也可以接受另一個名為 initiallyInactive 的值叔磷。如果使用這個值拢驾,
///任務(wù)不會被自動執(zhí)行,而是需要開發(fā)者手動去觸發(fā)改基。
let anotherQueue = DispatchQueue(label:"com.appcode.anotherQueue",
qos: .utility, attributes: .concurrent)
-
DispatchQueue
類的activate()
方法會讓任務(wù)開始執(zhí)行繁疤。注意,這個隊列并沒有被指定為并發(fā)隊列秕狰,因此它們會以串行的方式執(zhí)行稠腊。
let anotherQueue = DispatchQueue(label:"com.appcode.anotherQueue",
qos: .utility, attributes: .initiallyInactive)
anotherQueue.activate()
- 現(xiàn)在的問題是,我們?nèi)绾卧谥付?code>initiallyInactive 的同時將隊列指定為并發(fā)隊列鸣哀?其實很簡單架忌,我們可以將兩個值放入一個數(shù)組當中,作為
attributes
的參數(shù)我衬,替代原本指定的單一數(shù)值叹放。
let anotherQueue = DispatchQueue(label:"com.appcode.anotherQueue",
qos: .userInitiated, attributes: [.concurrent, .initiallyInactive])
延遲執(zhí)行
let delayQueue = DispatchQueue(label: "com.appcoda.delayqueue", qos: .userInitiated)
print(Date())
let additionalTime: DispatchTimeInterval = .seconds(2)
delayQueue.asyncAfter(deadline: .now() + additionalTime) {
print(Date())
}
全局隊列和主隊列
let globalQueue = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.main.async {
// Do something
}
DispatchWorkItem
-DispatchWorkItem
是一個代碼塊,它可以在任意一個隊列上被調(diào)用挠羔,因此它里面的代碼可以在后臺運行井仰,也可以在主線程運行。它的使用真的很簡單破加,就是一堆可以直接調(diào)用的代碼俱恶,而不用像之前一樣每次都寫一個代碼塊。
var value = 10
let workItem = DispatchWorkItem {
value += 5
}
workItem.perform()/// 這行代碼會在主線程上面調(diào)用
///其他隊列執(zhí)行
let queue = DispatchQueue.global()
queue.async {
workItem.perform()
}
queue.async(execute: workItem)
- 當一個任務(wù)項被調(diào)用后范舀,你可以通知主隊列(或者任何其它你想要的隊列)
workItem.notify(queue: DispatchQueue.main) {
print("value = ", value)
}
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)
}