Swift多线程:使用GCD实现异步下载图片1. GCD基础知识2. GCD的基础应用3. GCD的服务质量(优先级)

2018-06-28 13:41:04 浏览数 (1)

GCD属于系统及的线程管理,功能很强大,比上两次咱们分享的Operation要强大。有很多老前辈们已经创造了非常非常多的资料介绍GCD,因为大家都是把GCD放在了多线程内容分享的最开始,所以导致好多好多理论知识都被放在了GCD部分。

哈哈~幸好非典型技术宅英明神武的错峰出行,把一些基础概念放在了上两篇文章里面。极大的减轻了这篇文章的阅读负担。

既然前人都早了辣么多轮子,俺就不想再多介绍一些基础理论知识了。反正码再多的字,只会让大家立刻马上关掉这篇文章。而且上一篇关于Operation的阅读量就明显不高,看来大家不喜欢看啊。。。

那就容我偷偷懒嘛~重点还是分享一些代码吧。

不是说理论知识不重要啊,面试全都问这个。而且理论知识直接影响到对技术的理解深度,决定能在这条路上走多远。是会成为某个领域的大牛,还是只是简单的应用者。

1. GCD基础知识

纳尼?不是说不说基本概念了吗?easyeasyeasy~~只介绍一些那些最最重要的,不了解就会影响到阅读这篇文章的内容啦。

其实GCD和Operation很多地方惊人的相似。废话,都是多线程,底层都差不多,能不相似嘛!

GCD使用只需要两步:

  • STEP ONE:创建任务。
  • STEP TWO:把任务放进队列里。

。。。。。。!@#¥%……&*¥%#@!@#¥%…… 把大象放进冰箱里需要几步?!两步!打开冰箱门,把大象放进去!宅胖,现在很想抽死你啊!

确实真的就是这样的这只是为了骗你入门,让你觉得好简单

1.1 任务的分类

上面说了任务,任务只有两种方式:同步、异步。

  • 异步(asynchronous)具备开启新线程的能力,也具备跳过当前代码继续往下执行的能力。
  • 同步(synchronous)不具备开启新线程的能力,也不具备跳过当前代码继续往下执行的能力。

名称

开启新线程的能力

跳过当前代码继续往下执行的能力

异步

同步

NULL

NULL

换句话简单的说,异步任务就是可以同时开启多个跑道,同时跑好多辆车。同步就是只有一条车道,堵死也飞不过去,只能乖乖的等着,一辆接一辆。

任务放入到队列里面,会遵循first in first out原则。举个恶心的例子,就像是拉屎,先吃先拉,后吃后拉。 哈哈看了这个比方,别打死我

image.png

1.2 队列的分类

队列呐,也只有两种:串行队列(Serial Dispatch Queue)、并发队列(Concurrent Dispatch Queue)。

  • 串行队列(Serial Dispatch Queue): 让任务一个接着一个有序的执行,一个任务执行完毕后,再执行下一个任务。
  • 并发队列(Concurrent Dispatch Queue) 可以让多个任务同时执行,自动开启多个线程同时执行多个任务。

咦?有点晕,怎么感觉跟刚才的任务分类一样呐?没错!就是这样的。

下面为了让大家不要晕菜,我们把队列这个中文名字统一都叫做Queue,这样就和OperationQueue对应起来了,就不会那么晕了。

Serial QueueConcurrent 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 异步任务

代码语言:javascript复制
/// 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)")
        }
    }
}

image.png

我们看一下运行的结果,乱序打印的,并且没有在主线程中。这证明了确实是多个任务没有按照顺序执行。

2.2 main Queue 异步任务

代码语言:javascript复制
/// 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)")
            
        }
    }
}

image.png

我们看一下运行的结果,确实是顺序打印的。并且都执行在了主线程中。

2.3 小实践:实现异步下载图片

需求:异步下载一张图片,下载完成后显示在UI界面

实现后的效果图:

GCD.gif

思路:

  1. 在当前UI动作之外,开启一个global Queue 异步,用来下载图片。因为过程可能很耗时。
  2. 等下载完成后,开启一个main Queue 异步,把下载的图片赋值,刷新UI。

这个小Demo其实也实现了线程间通讯。

代码语言:javascript复制
@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里面也见到过,级别越高,就会给分配的资源越多。但是并不是严格按照级别的高低来执行的

image.png

这是一个枚举值:

代码语言:javascript复制
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基础貌似不太现实,又不想一篇文章过长,那就拆开吧。下次再说。

最后,所有的代码都放在这里了:gitHub 下载后给颗Star吧~ 么么哒~(~o ̄3 ̄)~ 爱你们~


0 人点赞