3、GCD-swift
GCD 一直以来是基于 c 语言的。
apple 为了使 GCD 使用更加的 swift 化。 对 GCD 进行了进行了改造。
以下所有代码都是基于 Swift3.0
先来段废话:
(不了解基本概念的建议看看, 了解的直接略过!)
GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。
GCD 中两个重要重要概念 —— 队列 & 任务
队列
队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。
队列的主要作用是用来存放任务。
GCD会自动将队列中的任务取出,放到对应的线程中执行。
**队列分类: **
-
串行队列(Serial Dispatch Queue): 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务
-
并发队列(Concurrent Dispatch Queue): 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务), 并发功能只有在异步(dispatch_async)函数下才有效
> 由于队列同步执行不具有开启线程的能力,异步执行才可以开启线程。
> 并发队列在异步执行才有效。
队列执行任务的方式:
-
同步:在当前线程中执行,当前代码不执行完,就不能够执行下一条代码。会阻塞当前线程。
-
异步:在另一条线程中执行(不用等待当前代码执行完,就能够执行下一条),不会阻塞当前线程。
常见队列:
为了方便 GCD 使用,苹果默认提供了主队列和全局队列。 我们不需要创建只需要获取。
- 主队列 (串行)
let mainQueue = DispatchQueue.main
- 全局队列 (并发)
let globalQueue = DispatchQueue.global()
全局队列默认是并发队列,在不进行第三方框架或者大型的商业应用开发,全局队列基本够用。
任务
需要执行操作, 任务是使用闭包( oc: block) 封装的代码块。
废话到此完毕!
1. 项目开发中 GCD 代码使用
print("DispatchQueue.main.sync: befor", Thread.current)
DispatchQueue.global().async {
print("DispatchQueue.global().async: Time task", Thread.current, "\n --: 耗时操作在后台线程中执行!")
DispatchQueue.main.async {
print("DispatchQueue.main.async: update UI", Thread.current, "\n --: 耗时操作执行完毕后在主线程更新 UI 界面!")
}
}
print("DispatchQueue.main.sync: after", Thread.current)
// 打印:
DispatchQueue.main.sync: befor <NSThread: 0x60800007abc0>{number = 1, name = main}
DispatchQueue.main.sync: after <NSThread: 0x60800007abc0>{number = 1, name = main}
DispatchQueue.global().async: Time task <NSThread: 0x6080002662c0>{number = 3, name = (null)}
--: 耗时操作在后台线程中执行!
DispatchQueue.main.async: update UI <NSThread: 0x60800007abc0>{number = 1, name = main}
--: 耗时操作执行完毕后在主线程更新 UI 界面!
/*
after 执行的先后顺序是不确定的。是由 GCD 决定。
这里牵扯到一个概念: 线程间通讯! 说白的就是在线程间进行数据(信号)的传递。
*/
如果你只是单纯的找 GCD 在 swift 中怎么使用,看到这里你基本可以关掉这篇博客,开心的写代码去了。
2. GCD 使用 —— 精讲
先来了解 GCD 在 Swift 中的变化
GCD 框架头文件改变
import Dispatch.base // 没有实质性内容
import Dispatch.block // 没有实质性内容
import Dispatch.data // 没有实质性内容
import Dispatch.group
import Dispatch.io
import Dispatch.object
import Dispatch.once // 没有实质性内容
import Dispatch.queue
import Dispatch.semaphore
import Dispatch.source
import Dispatch.time
import Dispatch
import os_object
// 在 swift3.0 中新加的, OS_object 继承自 NSObject
// 在 GCD 中所有的对象都间接的继承自 NSObject。
import os_object
open class OS_object : NSObject {
}
Swift 中最大的变化其实就是更加的面相对象了,使用更加的方便简洁,摆脱了 OC 时代函数式的使用方式。
GCD 使用先来个个人小的总结:
口诀:同步不开异步开,串行开1条,并行开多条。
来,跟着哥一起念。( 我怕你们打我,你们还是别念了,我解释一下。)
同步不开异步开,串行开1条,并行开多条。
单纯这么简单的看是有误解的, 在我的评论中就可以看到。
具体的意思是:
队列中的任务同步执行,队列就不具有开启线程的能力, 队列中的任务异步执行,队列就具有开启线程的能力。
(同步和异步执行决定的是否有开启线程的能力)
如果队列具 **有开启线程的能力 (队列任务异步执行) ** 且队列是 串行队列 ,那么将会 开启 1 条线程 。
如果队列具 **有开启线程的能力 (队列任务异步执行) ** 且队列是 并发队列 ,那么将会 开启 多 条线程 。开启线程的条数由 GCD 决定。
** ( 串行和并发决定的是开几条线程 ) **
** 如果真正理解了上面这些,多 GCD 的使用和面试基本没什么障碍。 **
2.1 全局队列
全局队列是获取的,不是程序员创建的。
为了方便 GCD 的使用,apple 默认为我们提供的。
全局队列默认是并发队列,在不是进行第三方框架或者大型的商业应用开发,全局队列基本够用。
全局 ( 并发 ) 队列异步执行 :
并发队列异步(不阻塞当前线程)执行(队列就具有开启线程的能力), 队列会开启多条线程。
任务异步执行不会阻塞当前线程,
befor 在最前,
after 在任意位置,
task 执行顺序不确定 —— 并发执行(index可以确认)。
task 并发执行 —— 并发执行(number可以确认)。
异步开线程 number 可以确定开启了多条线程
开的线程数由 GCD 决定。 可以看到线程的 number 有重复,是 GCD 对线程进行了复用。
func async() {
print("DispatchQueue.global().async: befor", Thread.current)
// 全局队列进行 10次异步
for index in 0..<10 {
DispatchQueue.global().async {
print("DispatchQueue.global().async: task:(taskIndex:\(index)", Thread.current)
}
}
print("DispatchQueue.global().async: after", Thread.current)
}
打印:
DispatchQueue.global().async: befor <NSThread: 0x60800006a8c0>{number = 1, name = main}
DispatchQueue.global().async: task:(taskIndex:1 <NSThread: 0x600000079780>{number = 3, name = (null)}
DispatchQueue.global().async: task:(taskIndex:2 <NSThread: 0x6000000797c0>{number = 4, name = (null)}
DispatchQueue.global().async: task:(taskIndex:0 <NSThread: 0x600000079880>{number = 5, name = (null)}
DispatchQueue.global().async: task:(taskIndex:3 <NSThread: 0x608000074e00>{number = 6, name = (null)}
DispatchQueue.global().async: task:(taskIndex:4 <NSThread: 0x600000079780>{number = 3, name = (null)}
DispatchQueue.global().async: task:(taskIndex:5 <NSThread: 0x6000000797c0>{number = 4, name = (null)}
DispatchQueue.global().async: after <NSThread: 0x60800006a8c0>{number = 1, name = main}
DispatchQueue.global().async: task:(taskIndex:6 <NSThread: 0x600000079880>{number = 5, name = (null)}
DispatchQueue.global().async: task:(taskIndex:7 <NSThread: 0x608000074e00>{number = 6, name = (null)}
DispatchQueue.global().async: task:(taskIndex:8 <NSThread: 0x600000079780>{number = 3, name = (null)}
DispatchQueue.global().async: task:(taskIndex:9 <NSThread: 0x6000000797c0>{number = 4, name = (null)}
全局 ( 并发 ) 队列同步执行 :
并发队列同步(阻塞当前线程)执行(队列就不具有开启线程的能力), 队列不会开启线程(代码都在主线程中执行)。
任务同步执行会阻塞当前线程,
befor 在最前,
after 在最后,
task 执行顺序确定 —— 阻塞。
同步没有开启线程 number 可以确定没有开启多条线程。所有的代码都在 主线程中执行。
func sync() {
print("DispatchQueue.global().sync: befor", Thread.current)
for index in 0..<10 {
DispatchQueue.global().sync {
print("DispatchQueue.global().sync: task:(taskIndex:\(index))", Thread.current)
}
}
print("DispatchQueue.global().sync: after", Thread.current)
}
打印:
DispatchQueue.global().sync: befor <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:0) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:1) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:2) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:3) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:4) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:5) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:6) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:7) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:8) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:9) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: after <NSThread: 0x60800007fa80>{number = 1, name = main}
2.2 主队列
主队列是获取的,不是程序员创建的,apple 默认为我们提供的。
(app 开发中,所有的 UI 更新操作都应该在主线程中进行)
主队列(串行)异步执行
主队列异步(不会阻塞当前线程)执行(队列就具有开启线程的能力), 队列会开启线程(开启的线程就是主线程)。
> 有朋友问我,异步会开启线程, 主队列异步就不会开启线程。
> 我当时还信以为真。认为自己错误,说特殊情况特殊处理。 其实说白了就是学艺不精。
> 由于主队列是我们获取的,不是我们创建的,在某种意识中会认为主线程不是在主队列中创建的。(认为一开始就存在的。)
> 任务异步执行不会阻塞当前线程,
befor 在最前,
after 在第二,
task 执行顺序确定 —— 串行执行(index可以确认)。
同步没有开启线程 number 可以确定没有开启多条线程。所有的代码都在 主线程中执行。
主队列异步的操作主要用在更新 UI 操作中。 具体参考 项目开发中 GCD 代码使用。
func async() {
print("DispatchQueue.main.async: befor", Thread.current)
for index in 0..<10 {
DispatchQueue.main.async {
print("DispatchQueue.main.async: task:(taskIndex:\(index)", Thread.current)
}
}
print("DispatchQueue.main.async: after", Thread.current)
}
打印:
DispatchQueue.main.async: befor <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: after <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:0 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:1 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:2 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:3 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:4 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:5 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:6 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:7 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:8 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:9 <NSThread: 0x60800006ddc0>{number = 1, name = main}
主队列(串行)同步执行
执行的效果就俩字 ** 死锁**
主线程同步,在 Swift 中,编译阶段就报错,在 oc 中是在运行的时候才能发现。体现的主要是界面的 “假死”。
Snip20170316_1.png
2.3 自定义队列
/*
label: 队列名称
qos: 服务质量
// 后台优先级
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
// 未指定
public static let unspecified: DispatchQoS
attributes: 队列属性
// 并发
public static let concurrent: DispatchQueue.Attributes
// 初始化不活跃
public static let initiallyInactive: DispatchQueue.Attributes
autoreleaseFrequency: 自动释放频率
public enum AutoreleaseFrequency {
// 继承
case inherit
// 工作项
case workItem
// 从来没有,永远不
case never
}
target: 目标队列
( 这些参数我也没搞明白)
*/
public convenience init(label: String,
qos: DispatchQoS = default,
attributes: DispatchQueue.Attributes = default,
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default,
target: DispatchQueue? = default)
在没有搞明白参数的时候,由于 swift 可以使用默认参数, 我们可以使用默认参数。
自定义(串行)队列异步执行
// 使用默认的构造函数创建了一个队列(并不知队列是串行还是并发)
// api 文档没有给够足够的信息。 后期的文档描述完整后可以直接查看文档。
func async() {
print("DispatchQueue(label: \"laughing\").async: befor", Thread.current)
let queue = DispatchQueue(label: "laughing")
for index in 0..<10 {
queue.async {
print("DispatchQueue(label: \"laughing\").async: task:(taskIndex:\(index)", Thread.current)
}
}
print("DispatchQueue(label: \"laughing\").async: after", Thread.current)
}
/*
这么写你会有一种串行队列并发执行的错觉。
for index in 0..<10 {
DispatchQueue(label: "laughing").sync {
print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
}
}
*/
打印:
DispatchQueue(label: "laughing").async: befor <NSThread: 0x608000067180>{number = 1, name = main}
DispatchQueue(label: "laughing").async: after <NSThread: 0x608000067180>{number = 1, name = main}
DispatchQueue(label: "laughing").async: task:(taskIndex:0 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:1 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:2 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:3 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:4 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:5 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:6 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:7 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:8 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:9 <NSThread: 0x608000074200>{number = 3, name = (null)}
** 由于只异步执行只开了1 条线程, 可只。 默认创建的队列是 串行队列**
自定义(串行)队列同步执行
串行队列同步执行,没有开启线程。 代码在主线程中执行。
func sync() {
print("DispatchQueue(label: \"laughing\").sync: befor", Thread.current)
let queue = DispatchQueue(label: "laughing")
for index in 0..<10 {
queue.sync {
print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
}
}
/*
这么写你会有一种串行队列并发执行的错觉。
for index in 0..<10 {
DispatchQueue(label: "laughing").sync {
print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
}
}
*/
print("DispatchQueue(label: \"laughing\").sync: after", Thread.current)
}
打印:
DispatchQueue(label: "laughing").sync: befor <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:0) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:1) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:2) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:3) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:4) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:5) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:6) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:7) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:8) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:9) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: after <NSThread: 0x600000071d40>{number = 1, name = main}
自定义(并发)队列异步执行
(参考全局队列)
func async1() {
print("DispatchQueue(label: \"laughing1\").async: befor", Thread.current)
let queue = DispatchQueue(label: "laughing1", attributes: DispatchQueue.Attributes.concurrent)
for index in 0..<10 {
queue.async {
print("DispatchQueue(label: \"laughing1\").async: task:(taskIndex:\(index)", Thread.current)
}
}
print("DispatchQueue(label: \"laughing1\").async: after", Thread.current)
}
打印
DispatchQueue(label: "laughing1").async: befor <NSThread: 0x600000261d80>{number = 1, name = main}
DispatchQueue(label: "laughing1").async: after <NSThread: 0x600000261d80>{number = 1, name = main}
DispatchQueue(label: "laughing1").async: task:(taskIndex:1 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:3 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:4 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:2 <NSThread: 0x60800026d9c0>{number = 4, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:5 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:7 <NSThread: 0x60800026d9c0>{number = 4, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:6 <NSThread: 0x60800026da00>{number = 5, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:0 <NSThread: 0x60800026db40>{number = 6, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:8 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:9 <NSThread: 0x60800026d9c0>{number = 4, name = (null)}
自定义(并发)队列同步执行
func sync1() {
print("DispatchQueue(label: \"laughing1\").sync: befor", Thread.current)
let queue = DispatchQueue(label: "laughing1", attributes: DispatchQueue.Attributes.concurrent)
for index in 0..<10 {
queue.sync {
print("DispatchQueue(label: \"laughing1\").sync: task:(taskIndex:\(index))", Thread.current)
}
}
/*
这么写你会有一种串行队列并发执行的错觉。
for index in 0..<10 {
DispatchQueue(label: "laughing").sync {
print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
}
}
*/
print("DispatchQueue(label: \"laughing1\").sync: after", Thread.current)
}
打印
DispatchQueue(label: "laughing1").sync: befor <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:0) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:1) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:2) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:3) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:4) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:5) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:6) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:7) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:8) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: task:(taskIndex:9) <NSThread: 0x608000061cc0>{number = 1, name = main}
DispatchQueue(label: "laughing1").sync: after <NSThread: 0x608000061cc0>{number = 1, name = main}