GCD 属于系统及的线程管理, 功能很强大, 比上两次咱们分享的 Operation 要强大有很多老前辈们已经创造了非常非常多的资料介绍 GCD, 因为大家都是把 GCD 放在了多线程内容分享的最开始, 所以导致好多好多理论知识都被放在了 GCD 部分
哈哈~ 幸好非典型技术宅英明神武的错峰出行, 把一些基础概念放在了上两篇文章里面极大的减轻了这篇文章的阅读负担
既然前人都早了辣么多轮子, 俺就不想再多介绍一些基础理论知识了反正码再多的字, 只会让大家立刻马上关掉这篇文章而且上一篇关于 Operation 的阅读量就明显不高, 看来大家不喜欢看啊
那就容我偷偷懒嘛~ 重点还是分享一些代码吧
不是说理论知识不重要啊, 面试全都问这个而且理论知识直接影响到对技术的理解深度, 决定能在这条路上走多远是会成为某个领域的大牛, 还是只是简单的应用者
1. GCD 基础知识
纳尼? 不是说不说基本概念了吗? easy~easy~easy~~~ 只介绍一些那些最最重要的, 不了解就会影响到阅读这篇文章的内容啦
其实 GCD 和 Operation 很多地方惊人的相似废话, 都是多线程, 底层都差不多, 能不相似嘛!
GCD 使用只需要两步:
STEP ONE: 创建任务
STEP TWO: 把任务放进队列里
~!@#%&*%#@!~@#% 把大象放进冰箱里需要几步?! 两步! 打开冰箱门, 把大象放进去! 宅胖, 现在很想抽死你啊!
确实真的就是这样的内心 OS: 这只是为了骗你入门, 让你觉得好简单
1.1 任务的分类
上面说了任务, 任务只有两种方式: 同步异步
异步 (asynchronous) 具备开启新线程的能力, 也具备跳过当前代码继续往下执行的能力
同步 (synchronous) 不具备开启新线程的能力, 也不具备跳过当前代码继续往下执行的能力
名称 | 开启新线程的能力 | 跳过当前代码继续往下执行的能力 |
---|---|---|
异步 | 有 | 有 |
同步 | NULL | NULL |
换句话简单的说, 异步任务就是可以同时开启多个跑道, 同时跑好多辆车同步就是只有一条车道, 堵死也飞不过去, 只能乖乖的等着, 一辆接一辆
任务放入到队列里面, 会遵循 first in first out 原则举个恶心的例子, 就像是拉屎, 先吃先拉, 后吃后拉 哈哈~ 看了这个比方, 别打死我~
1.2 队列的分类
队列呐, 也只有两种: 串行队列(
Serial Dispatch Queue
)并发队列(
- Concurrent Dispatch Queue
- )
串行队列(
Serial Dispatch Queue
): 让任务一个接着一个有序的执行, 一个任务执行完毕后, 再执行下一个任务
并发队列(
Concurrent Dispatch Queue
) 可以让多个任务同时执行, 自动开启多个线程同时执行多个任务
咦? 有点晕, 怎么感觉跟刚才的任务分类一样呐? 没错! 就是这样的
下面为了让大家不要晕菜, 我们把队列这个中文名字统一都叫做 Queue, 这样就和 OperationQueue 对应起来了, 就不会那么晕了
Serial Queue 和 Concurrent Queue 各自都有一个特殊的 Queue
主队列(main queue): 是 Serial Queue 中特殊的一种只能在主线程中进行, 并且主队列里面的任务, 只有当主线程空闲的时候才能被执行用来刷新 UI 使用
全局队列(global queue): 是 Concurrent Queue 中特殊的一种用来执行耗时操作
同时, GCD 里面还可以自定义 Queue
1.3 排列组合开始
最开始的时候, 咱们是不是说了, 使用 GCD 就只有两步: 创建任务, 把任务放进 Queue 里
任务有两种: 同步异步 Queue 加上两种特殊的 (不包括自定义的) 一共有四种来吧, 开始排列组合吧有八种吧
名称 | 能够开启新线程 | 能够跳过当前代码继续进行 |
---|---|---|
异步 | 能 | 能 |
同步 | / | / |
Queue | 串行队列 Serial | 并行队列 concurrent | 主队列 main | 全局队列 global |
---|---|---|---|---|
能够多个任务同时执行 | / | 能 | / | 能 |
哈哈哈 O(_)O 哈哈~
彻底晕菜
oooO Oooo ( 踩)你 (死 ) \ ( ) / _) (_/
来吧, 直接告诉你结论吧里面有几个特例
☺ | 串行队列 Serial Queue | 并行队列 concurrent Queue | 主队列 main Queue | 全局队列 global Queue |
---|---|---|---|---|
异步 | 新线程 、串行执行 | 新线程 、 并行执行 | 无新线程、串行执行 | 新线程 、 并行执行 |
同步 | 无新线程、串行执行 | 无新线程、串行执行 | 会锁死 | 无新线程、串行执行 |
看上面这个表, 所以如果想要同时做事情, 当然不能选同步任务啦因为它完全没能力! 搞不好还会造成锁死
要想同时做事情, 就选 concurrent Queue + 异步, 或者 global Queue + 异步 不过人家 global Queue 本来就是 concurrent Queue 特殊的一种
如果有多任务, 工作中最最省事儿常用的就是 global Queue + 异步单任务刷新 UI 就用 main Queue + 异步
上面都没心思看也没关系工作中, 如果有多任务, 首选 global Queue + 异步单任务刷新 UI 就用 main Queue + 异步
2. GCD 的基础应用
我滴妈妈~ 经过上面的分析, 最后, 最基础的使用就两种了 多任务: global Queue + 异步 单任务刷新 UI 就用 main Queue + 异步
说实话, 我也是第一次这么大胆的简化会不会被大神们拍死? 坐等~~~~
2.1 global Queue + 异步任务
- /// global Queue + 异步任务
- @IBAction func globalAsyn(_ sender: Any) {
- // 创建一个全局队列
- //get a global queue
- let globalQueue = DispatchQueue.global() for i in 0...10 {
- // 使用全局队列, 开启异步任务
- //use the global queue , run in asynchronous
- globalQueue.async {
- print("I am No.\(i), current thread name is:\(Thread.current)")
- }
- }
- }
我们看一下运行的结果, 乱序打印的, 并且没有在主线程中这证明了确实是多个任务没有按照顺序执行
2.2 main Queue + 异步任务
- /// main Queue + 异步任务
- @IBAction func mainAsyn(_ sender: Any) {
- // 创建一个主队列
- //get a main queue
- let mainQueue = DispatchQueue.main
- for i in 0...10 {
- // 使用主队列, 开启异步任务
- //use the main queue, run in asynchronous
- mainQueue.async {
- print("I am No.\(i), current thread name is:\(Thread.current)")
- }
- }
- }
我们看一下运行的结果, 确实是顺序打印的并且都执行在了主线程中
2.3 小实践: 实现异步下载图片
需求: 异步下载一张图片, 下载完成后显示在 UI 界面
实现后的效果图:
思路:
在当前 UI 动作之外, 开启一个 global Queue + 异步, 用来下载图片因为过程可能很耗时
等下载完成后, 开启一个 main Queue + 异步, 把下载的图片赋值, 刷新 UI
这个小 Demo 其实也实现了线程间通讯
- @IBAction func asynDownloadImage(_ sender: Any) {
- let imageVC = ImageVC()
- DispatchQueue.global().async {
- if let url = URL.init(string: "https://placebeard.it/355/140") {
- do {
- let imageData = try Data(contentsOf: url)
- let image = UIImage(data: imageData)
- // 因为宅胖家网络很好, 为了模拟网络很耗时, 就用了延时加载
- DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(2), execute: {
- imageVC.imageView.image = image
- imageVC.imageView .sizeToFit()
- })
- } catch {
- print(error)
- }
- }
- }
- navigationController?.pushViewController(imageVC, animated: true)
- }
3. GCD 的服务质量(优先级)
DispatchQoS.QoSClass
是在 Swift 中封装的关于描述服务质量的类
这个在 Operation 里面也见到过, 级别越高, 就会给分配的资源越多但是并不是严格按照级别的高低来执行的
这是一个枚举值:
- public enum QoSClass {
- case background // 后台默默执行, The background quality of service class.
- case utility // 通用的, The utility quality of service class.
- case `default` // 默认值, The default quality of service class.
- case userInitiated // 用户发起的, The user-initiated quality of service class.
- case userInteractive // 用来执行用户交互, The user-interactive quality of service class.
- case unspecified // 没啥重要事情, The absence of a quality of service class.
- public init?(rawValue: qos_class_t)
- public var rawValue: qos_class_t { get }
- }
看到上面的枚举值, 也大概能猜出来优先级的高低了和界面相关的用户的肯定是高的, 后台默默执行的肯定是低的
从高到低的顺序分别是: userInteractive -> userInitiated -> default -> utility -> background -> unspecified
最基本的基础基本上就到这里了掂量了一下, 还有调度组信号量阻塞等等都还没写这时候发现一篇写完 GCD 基础貌似不太现实, 又不想一篇文章过长, 那就拆开吧下次再说
来源: https://juejin.im/post/5a8539436fb9a0633611583d