GCD(Grand Central Dispatch)
核心是dispatch queue,队列就是一系列的代码块(WorkItem任务项).一般耗时和需要CPU大量计算的时候要分在后台线程,更新UI操作要放在主线程.
-
创建dispatch queue
let queue = DispatchQueue(label: "com.eric.myqueue")
最好为队列创建label.反向DNS
com.eric.myqueue
-
使用
sync
同步,async
异步执行//同步执行 queue.sync { for i in 0..<10 { print("🔴", i) } } //异步执行 queue.async { for i in 0..<10 { print("🔴", i) } }
Quality Of Service(QoS) 和优先级
由于主队列总是用来处理 UI 以及界面的响应,所以在主线程执行的任务永远都有最高的优先级
用于指定任务重要程度以及优先级的信息,在 GCD 中被称为 Quality of Service(QoS)
优先级从大到小
userInteractive
userInitiated
default
utility
background
unspecified
主队列执行的任务有最高优先级
优先级队列初始化方法:
let queue = DispatchQueue(label: "com.appcoda.queue1", qos: DispatchQoS.userInitiated)
并行队列
使用attributes
属性设置concurrent
并发队列
let anotherQueue = DispatchQueue(label: "com.appcoda.anotherQueue", qos: .utility, attributes: .concurrent)
inactiveQueue = anotherQueue
这个
attributes
参数也可以接受另一个名为initiallyInactive
的值。如果使用这个值,任务不会被自动执行,而是需要开发者手动去触发。
DispatchQueue
类的activate()
方法会让任务开始执行。注意,这个队列并没有被指定为并行队列,因此它们会以串行的方式执行
//手动触发
if let queue = inactiveQueue {
queue.activate()
}
现在的问题是,我们如何在指定 initiallyInactive
的同时将队列指定为并行队列?其实很简单,我们可以将两个值放入一个数组当中,作为 attributes
的参数,替代原本指定的单一数值:
let anotherQueue = DispatchQueue(label: "com.appcoda.anotherQueue", qos: .userInitiated, attributes: [.concurrent, .initiallyInactive])
延迟执行
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())
}
除此之外,我们还有别的方法可以用来指定执行时间。如果不想使用任务预定义的方法,你可以直接使用一个 Double 类型的值添加到当前时间上:
delayQueue.asyncAfter(deadline: .now() + 0.75) {
print(Date())
}
Tips:
指定一个 TimeInterval,也就是以秒为单位的整数或者分数形式
extension DispatchTime: ExpressibleByIntegerLiteral { public init(integerLiteral value: Int) { self = DispatchTime.now() + .seconds(value) } } extension DispatchTime: ExpressibleByFloatLiteral { public init(floatLiteral value: Double) { self = DispatchTime.now() + .milliseconds(Int(value * 1000)) } }
DispatchQueue.main.asyncAfter(deadline: 5) { /* ... */ }
访问主队列和全局队列
操作系统会创建一个后台队列的集合,也被称为全局队列(global queue)
let globalQueue = DispatchQueue.global()
当使用全局队列的时候,并没有太多的属性可供我们进行修改。但是,你仍然可以指定你想要使用队列的 Quality of Service:
let globalQueue = DispatchQueue.global(qos: .userInitiated)
主队列,通常用于更新UI
DispatchQueue.main.async {
// Do something
}
使用DispatchWorkItem 对象
DispatchWorkItem 是一个代码块,它可以在任意一个队列上被调用,因此它里面的代码可以在后台运行,也可以在主线程运行。它的使用真的很简单,就是一堆可以直接调用的代码,而不用像之前一样每次都写一个代码块
let workItem = DispatchWorkItem {
// Do something
}
执行
workItem.perform()
上行代码在主线程调用,也可以用其它队列执行
let queue = DispatchQueue.global()
queue.async {
workItem.perform()
}
快捷执行
queue.async(execute: workItem)
当一个任务项被调用后,你可以通知主队列(或者任何其它你想要的队列),如下所示:
workItem.notify(queue: DispatchQueue.main) {
print("value = ", value)
}
注意点
-
print("1") DispatchQueue.main.sync { print("2") } print("3")
会中断线程,Error
-
print("1") DispatchQueue.main.async { print("2") } print("3")
执行顺序1->3->2
参考: Swift.gg