當(dāng)今App中多線程是必不可少的因素给梅,而GCD起著舉足輕重的作用。
下面我們來看看swift 3.0中的GCD瓢娜,能給我們帶來哪些驚喜吧识窿!
dispatch_async
GCD最常用的模式就是在全局隊列執(zhí)行任務(wù)主線程中刷新UI
DispatchQueue.global(qos: .default).async {
print("task" + "\(Thread.current)")
//主線程異步執(zhí)行 刷新UI
DispatchQueue.main.async {
print("UI - task" + "\(Thread.current) ")
}
}
Queue attributes
由于.serial不在是DispatchQueue.Attributes中的屬性,只留下.concurrent第步。下面我們通過代碼驗證一下
//創(chuàng)建并發(fā)隊列
let concurrentQueue = DispatchQueue(label: "mkiltech.com",attributes: .concurrent)
//創(chuàng)建串行隊列
let serialQueue = DispatchQueue(label: "mkiltech.com")
//串行異步
serialQueue.async {
sleep(2)
print("serialTask 1" + "\(Thread.current) ")
}
serialQueue.async {
print("serialTask 2" + "\(Thread.current) ")
}
//并發(fā)異步
concurrentQueue.async {
sleep(2)
print("concurrentTask 1" + "\(Thread.current) ")
}
concurrentQueue.async {
print("concurrentTask 2" + "\(Thread.current) ")
}
// 打印結(jié)果
concurrentTask 2<NSThread: 0x60800007d640>{number = 3, name = (null)}
concurrentTask 1<NSThread: 0x60800007e340>{number = 4, name = (null)}
serialTask 1<NSThread: 0x600000078f00>{number = 5, name = (null)}
serialTask 2<NSThread: 0x600000078f00>{number = 5, name = (null)}
可以看出疮装,默認(rèn)情況下創(chuàng)建的是串行隊列,指定.concurrent為并發(fā)隊列粘都。
下面來看看隊列完整的初始化方法
public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)
@param qos 隊列優(yōu)先級
新的隊列優(yōu)先級替換了在iOS8中被廢棄的舊的優(yōu)先級屬性,顯得更加直觀友好,這是在 QOS中的調(diào)用列表:
* - DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED (.userInitiated)
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT (.default)
* - DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY (.utility)
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND (.background)
@param attributes 串行隊列并發(fā)隊列廓推,上面已說明
@param autoreleaseFrequency
下面是一段官方代碼注釋,由于沒有找到相關(guān)官方文檔說明翩隧,理解可能有偏差樊展。
/*!
* @typedef dispatch_autorelease_frequency_t
* Values to pass to the dispatch_queue_attr_make_with_autorelease_frequency()
* function.
*
* @const DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
* Dispatch queues with this autorelease frequency inherit the behavior from
* their target queue. This is the default behavior for manually created queues.
*
* @const DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
* Dispatch queues with this autorelease frequency push and pop an autorelease
* pool around the execution of every block that was submitted to it
* asynchronously.
* @see dispatch_queue_attr_make_with_autorelease_frequency().
*
* @const DISPATCH_AUTORELEASE_FREQUENCY_NEVER
* Dispatch queues with this autorelease frequency never set up an individual
* autorelease pool around the execution of a block that is submitted to it
* asynchronously. This is the behavior of the global concurrent queues.
*/
public enum AutoreleaseFrequency {
case inherit
case workItem
case never
}
我們看到DispatchQueue.AutoreleaseFrequency
有三種屬性值.inherit
、.workItem
和.never
,稍微研究了一下GCD
和Autorelease
以前堆生,DispatchQueues
將在未指定的時間(當(dāng)線程變?yōu)椴换顒訒r)彈出它們的自動釋放池专缠。 在實(shí)踐中,這意味著您為每個提交的調(diào)度項目創(chuàng)建了一個自動釋放池淑仆,或者您的自動釋放的對象將掛起不可預(yù)測的時間量涝婉。
非確定性不是什么好事情(特別是在并發(fā)庫!)蔗怠,所以現(xiàn)在Apple允許你指定三種行為之一:
.inherit
:不確定墩弯,之前默認(rèn)的行為也是現(xiàn)在的默認(rèn)值
.workItem
:為每個執(zhí)行的項目創(chuàng)建和排除自動釋放池,項目完成時清理臨時對象
.never
:GCD不為您管理自動釋放池
DispatchTime 延時執(zhí)行
let delay = DispatchTime.now() + .seconds(60)
DispatchQueue.main.asyncAfter(deadline: delay) {
// task 延時執(zhí)行
}
也可以直接+
一個秒數(shù)
let two = DispatchTime.now() + 2.0
因為DispatchTime
中自定義了這些運(yùn)算符吩跋。
public func +(time: DispatchWallTime, seconds: Double) -> DispatchWallTime
public func -(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime
public func <(a: DispatchTime, b: DispatchTime) -> Bool
public func ==(a: DispatchTime, b: DispatchTime) -> Bool
DispatchGroup
如果想在dispatch_queue
中所有的任務(wù)執(zhí)行完成后再做某種操作可以使用DispatchGroup
。原先的dispatch_group_t
由現(xiàn)在的DispatchGroup
對象代替渔工。
let group = DispatchGroup()
let queueDownload1 = DispatchQueue(label: "com.Download1")
queueDownload1.async(group: group) {
// up task 1
}
let queueDownload2 = DispatchQueue(label: "com.Download2")
queueDownload2.async(group: group) {
// up task 2
}
group.notify(queue: DispatchQueue.main) {
// 等待下載完成
}
DispatchGroup
會在組里的操作都完成后執(zhí)行notify
锌钮。
DispatchWorkItem
使用DispatchWorkItem
代替原來的dispatch_block_t
。 在DispatchQueue
執(zhí)行操作除了直接傳了一個() -> Void
類型的閉包外引矩,還可以傳入一個DispatchWorkItem
梁丘。
public func sync(execute workItem: DispatchWorkItem)
public func async(execute workItem: DispatchWorkItem)
DispatchWorkItem
的初始化方法可以配置Qos
和DispatchWorkItemFlags
,但是這兩個參數(shù)都有默認(rèn)參數(shù)旺韭,所以也可以只傳入一個閉包兰吟。
public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> ())
DispatchWorkItemFlags
枚舉中assignCurrentContext
表示QoS
根據(jù)創(chuàng)建時的context
決定。 值得一提的是DispatchWorkItem
也有wait
方法茂翔,使用方式和group
一樣。調(diào)用會等待這個workItem
執(zhí)行完履腋。
let myQueue = DispatchQueue(label: "mkil.queue", attributes: .concurrent)let workItem = DispatchWorkItem { sleep(1) print("done")}myQueue.async(execute: workItem)print("before waiting")workItem.wait()print("after waiting")
barrier
假設(shè)我們有一個并發(fā)的隊列用來讀寫一個數(shù)據(jù)對象珊燎。如果這個隊列里的操作是讀的,那么可以多個同時進(jìn)行遵湖。如果有寫的操作悔政,則必須保證在執(zhí)行寫入操作時,不會有讀取操作在執(zhí)行延旧,必須等待寫入完成后才能讀取谋国,否則就可能會出現(xiàn)讀到的數(shù)據(jù)不對。在之前我們用dipatch_barrier
實(shí)現(xiàn)迁沫。 現(xiàn)在屬性放在了DispatchWorkItemFlags
里芦瘾。
let wirte = DispatchWorkItem(flags: .barrier) {
// write data
}
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
dataQueue.async(execute: wirte)
DispatchSemaphore
為了線程安全的統(tǒng)計數(shù)量,我們會使用信號量作計數(shù)集畅。原來的dispatch_semaphore_t
現(xiàn)在用DispatchSemaphore
對象表示近弟。 初始化方法只有一個,傳入一個Int
類型的數(shù)挺智。
let semaphore = DispatchSemaphore(value: 5)
// 信號量減一
semaphore.wait()
//信號量加一
semaphore.signal()
dispatch_once
在Swift3中,dispatch_once被廢棄了,應(yīng)該被替換成了其他全局或者靜態(tài)變量和常量.
class MyManager {
public static let shareInstance = MyManager()
}
let manager = MyManager.shareInstance
dispatch_assert
這個也是今年蘋果在OS發(fā)布的新技術(shù),線程的先決條件.這個在你執(zhí)行代碼前可以檢查當(dāng)前線程是否是你希望的線程.
enum DispatchPredicate {
//Predicates that the evaluated context is the associated dispatch queue.
case onQueue(DispatchQueue)
//Predicates that the evaluated context is the associated dispatch queue as part of a barrier operation.
case onQueueAsBarrier(DispatchQueue)
//Predicates that the evaluated context is not the associated dispatch queue.
case notOnQueue(DispatchQueue)
// onQueueAsBarrier
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
let wirte = DispatchWorkItem(flags: .barrier) {
dispatchPrecondition(condition: .onQueueAsBarrier(dataQueue))
// write data
}
dataQueue.async(execute: wirte)
let queue = DispatchQueue.global()
let mainQueue = DispatchQueue.main
// .notOnQueue
mainQueue.async {
dispatchPrecondition(condition: .notOnQueue(mainQueue))
// This code won't execute
}
// onQueue
queue.async {
dispatchPrecondition(condition: .onQueue(queue))
// This code will execute
}
}
參考文檔
https://developer.apple.com/videos/play/wwdc2016/720/
http://gold.xitu.io/post/57f6677e128fe100544dc3cb
https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md