Swift多线程之Operation:按优先级加载图片

乖乖的,俺又来了。上一个系列写传感器,特别庆幸自己在开篇的时候就立下了一个Flag,不然那个系列估计到现在就不知道被自己偏到什么地方去了。众所周知的iOS有好多传感器,配合各种传感器可以做出来各种好玩的东东。

宅胖也见过很多超牛的作品用传感器和动画相结合,那简直了。

所以,这个系列,叫做多线程。在写之前还是稍稍做了一下功课,大概看了看一些前辈们的分享帖。于是果断的决定这个系列不出意外,基本上就用Swift来写了。如果有特别强烈要求的同学,打算重金打赏宅胖两块钱要求提供OC源代码的,我也会毫无底线的答应这个无理高瞻远瞩、英明神武的要求。

image.png

这张配图是个什么鬼❓对啊,多线程就是这样的啊,玩死玩死玩死你。╭(╯^╰)╮哼!

其实确实是因为Swift关于多线程的分享太少了,OC的倒是有很多。再说了,Swift和OC是好兄弟,好基友嘛~都差不多。

计划呢还是八篇。咳咳~对,就是八篇。上个系列也是计划八篇,最后还是无情的扔下了蓝牙,只剩下了传感器小兄弟。

image.png

通过宅胖的善变说明了一个问题,需求嘛,写出来就是用来改的。谁说只有产品经理会改需求,程序员也会改好嘛!有本事咱们比比!

这个系列计划是按照下面的脚本进行的。以下目录随时修改仅供参考。

第一篇:Operation 基础
第二篇:Operation 实例

第三篇:GCD 基础
第四篇:GCD 实例

第五篇:NSThread & pthread 的介绍

第六篇:Runloop 基础
第七篇:Runloop 实战

第八篇:大综合例子

哔哔哔,三十分钟过去了。

好了,我们改需求吧。第一篇、第二篇要求该内容。变更为:
第一篇:Operation 基本应用及优先级小案例
第二篇:Operation 依赖关系及综合小案例

好奇怪啊,别人都是最后才分享NSOperation,你怎么一上来就说Operation(=@__@=)?
没错,就是一上来就搞这个。因为最简单,用起来最容易啊。一上来搞一堆理论知识,很快大家就都跑了,还怎么勾搭人啊。

然后宅胖又打开了四月份在写iOS动画系列的时候准备好的多线程大纲,然后....然后.....又默默的合上了。居然是OC的,居然有些地方通篇都是理论。又没稿费,写那么多凑字数干啥!@¥#%T$!@

来吧,撸起袖子,全新开始Operation吧。

1. 进程和线程

1.1 进程

  • 进程:正在运行的应用程序叫进程
  • 进程之间都是独立的,运行在专用且受保护的内存空间中
  • 两个进程之间无法通讯

通俗的理解,手机上同时开启了两个App。这两个App肯定是在不同的进程中的。所以这两个App之间是独立的,内存中的数据不能互相窜来窜去,两个App之间也没有办法进行通讯。

等等,你说啥?两个App之间没有办法进行通讯?嗯,我说的是正常情况下。当然还是有不正常情况啊,例如使用iOS提供的极少数的几种进程间通讯的工具。

1.2 线程

  • 线程:进程想要执行任务,必须要有线程,每个进程至少有一条线程。
  • 线程就是用来干活的。
  • 程序一启动,就会启动进程。进程默认开启一条线程。

干活的线程?对啊,活太多,或者不想彼此互相等着浪费时间,当然可以找几个人同时干了。
例如在项目上,产品经理需求都没有完全写完,也不妨碍先设计一个大概的数据框架啊。例如需求没有完全定下来,不妨碍UI童鞋提前设计啊,大不了再改嘛~ HOHO~怎么听上去都像是黑话。

1.3 多线程

  • 单核CPU同一时间,CPU只能处理1个线程,只有1个线程在执行任务。

  • 多线程的同时执行 : 其实是CPU在多条线程之间快速切换(调度任务)。

  • 如果CPU调度线程的速度足够快,就造成了多线程同时执行的假象

  • 如果线程非常多,CPU会在多条线程之间不断的调度任务,结果就是消耗了大量的CPU资源,效率下降:

    • 每个线程调度的频率会降低
    • 线程的执行效率会下降

iPhone手机是几核的?
A7 : iPhone 5S , 双核
A8: iPhone 6、iPhone 6 Plus,双核
A9:iPhone 6S、iPhone 6S Plus,双核
A10:iPhone 7、iPhone 7 Plus,2+2核

1.4 iOS中的多线程

刚才说了,iOS App一旦运行,默认就会开启一条线程。这条线程,我们通常称作为“主线程

主线程的作用:

  1. 刷新UI
  2. 处理UI事件,例如点击、滚动、拖拽。

如果主线程的操作太多、太耗时,就会造成App卡顿现象严重。所以,通常我们都会把耗时的操作放在子线程中进行,获取到结果之后,回到主线程去刷新UI。

2. Operation

来来来,我们就快进到了这里。这个是苹果推荐使用的一种多线程技术,好处是不用关心线程及线程的生命周期。

image.png

看完这个导图,是不是确实觉得Operation简单?属性、方法没有那么多。

我们来看看基础使用:

//    最基本的使用Operation
    private func basicOperation() {
//        第一步:创建Operation
        let op = Operation.init()
//        第二步:把要执行的代码放入operation中
        op.completionBlock = {
        
            print(#function,#line,Thread.current)
        }
//        第三步:创建OperationQueue
        let opQueue = OperationQueue.init()
//        第四步:把Operation加入到线程中
        opQueue.addOperation(op)
    }

使用BlockOperation创建operatoin,并直接运行。咱们看看会在哪条线程执行。

//创建一个简单的BlockOperation
private func CreatBasicBlockOperation() {
    //使用BlockOperation创建operation
    let operation = BlockOperation.init {
        //打印,看看在哪个线程中
        
        print(#function,#line,Thread.current)
    }
    
    //直接运行operation,看看运行在哪个线程中
    operation.start()
}

我们来打印一下看看运行的结果:

![image.png](http://upload-images.jianshu.io/upload_images/2248583-7885c746c8a53ed2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800

Operation Demos.gif

)

看到了咩?这就是我们说,创建完Operation如果直接运行,就会在当前线程执行。也就是说,如果实在主线程创建并且start的,那就会在主线程执行;如果是在子线程创建并且start的,那就会在子线程执行。

3. Basic Demo

在这个例子里面,需求如下:
1,在子线程加载每个图片的数据
2,图片数据下载完毕之后,显示出来
3,开始请求数据的时候,让指示符开始转动
4,所有图片下载完毕后,指示符停止转动

3.1 线程最大任务数

在OperationQueue中,maxConcurrentOperationCount 这个属性是限制同时执行的任务数.
比如,最大并发数设置成3,队列就会保证只同时执行3个任务.从而间接的控制了线程的数量。

线程可以复用,而且在线程回收的间隙可以及时的准备线程保证并发性。
注意:队列最大并发数不是线程数!!! 再纠结这个问题,打屁屁!

3.2 Swift中的do catch

这是什么鬼?嗯,这个是Swift和OC不一样的地方。Swift中出现了可选值这么一个东西,这个不是这次的重点。想深入了解的童鞋可以参看这篇:Swift可选值(Optional Values)介绍:?和!使用总结 。感谢@kmplayer 童鞋。

Swift 里有四种方法来处理错误:

  1. 把错误从函数传递到调用函数的代码里
  2. 使用一个 do-catch 语句来处理错误
  3. 把错误当做一个可选值来处理
  4. 断言这个错误不会发生

因为Demo里面用到了do catch,那咱们就只说do catch.
在Swift的标准try中,是要配合do catch的。

下面是do-catch语句的一般格式,如果do分句内的代码抛出了一个错误,它就被catch分句捕获,并判断由哪个分句来处理此错误。

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
}

3.3 优先级

细心地大大们一定发现了,在思维导图里面出现了两个优先级。一个是属于Operation 的,一个是属于OperationQueue的。那咱们分看看看这两个都是啥。

3.3.1 Operation中的优先级

Operation里面的这个叫做qualityOfService

public enum QualityOfService : Int {
    case userInteractive
    case userInitiated    
    case utility    
    case background    
    case `default`
}
  • userInteractive:最高优先级,用于用户交互事件
  • userInitiated :次高优先级,用于用户需要马上执行的事件
  • utility:普通优先级,用于普通任务
  • background:最低优先级,用于不重要的任务
  • default:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级

3.3.2 operationQueue 里面的优先级

operationQueue中表示优先级的属性是queuePriority,表示操作在队列中的优先级。

public enum QueuePriority : Int {
    case veryLow
    case low
    case normal
    case high
    case veryHigh
}

这些优先级都是相对的,并不是是说必须要执行完最高的才执行次重要的。这里面并没有一个特别严格顺序。只是在分配资源上有倾向性。如果队列需要有严格的执行顺序,还是要添加依赖关系的,这个是我们下一篇文章要分享的内容。

4. 案例实现

Operation 基本应用及优先级小案例。
实现后效果如下:

Operation Demos.gif

还是老样子,只说最重点的。小细节地方,大家有兴趣可以去看源代码。

这个Demo里面,用了两种方法创建Operation。

startBasicDemo,使用的是闭包创建Operation的方式。在startPriorityDemo里面使用的是自定义的构造方法创建的Operation,然后把任务数组加入到线程中。

基本应用:

fileprivate func startBasicDemo() {
    operationQueue.maxConcurrentOperationCount = 3
    
    
    activityIndicator.startAnimating()
    
    //        使用数组给图片赋值
    //        use Array set image
    for imageView in imageViews! {
        operationQueue.addOperation {
            if let url = URL(string: "https://placebeard.it/355/140") {
                do {
                    let image = UIImage(data:try Data(contentsOf: url))
                    
                    DispatchQueue.main.async {
                        imageView.image = image
                    }
                } catch {
                    print(error)
                }
            }
        }
    }
    
    
    //        global queue
    DispatchQueue.global().async {
        [weak self] in
        
        //            等待所有操作都完成了,回到主线程停止刷新器。
        //            wait Until All Operations are finished, then stop animation of activity indicator
        self?.operationQueue.waitUntilAllOperationsAreFinished()
        DispatchQueue.main.async {
            
            self?.activityIndicator.stopAnimating()
        }
    }
}

设置了优先级的Demo:

fileprivate func startPriorityDemo() {
    operationQueue.maxConcurrentOperationCount = 2
    activityIndicator.startAnimating()
    
    var operations = [Operation]()
    for (index, imageView) in (imageViews?.enumerated())! {
        if let url = URL(string: "https://placebeard.it/355/140") {
            //                使用构造方法创建operation
            let operation = convenienceOperation(setImageView: imageView, withURL: url)
            
            //根据索引设置优先级
            switch index {
            case 0:
                operation.queuePriority = .veryHigh
            case 1:
                operation.queuePriority = .high
            case 2:
                operation.queuePriority = .normal
            case 3:
                operation.queuePriority = .low
            default:
                operation.queuePriority = .veryLow
            }
            
            operations.append(operation)
        }
    }
    
    //        把任务数组加入到线程中
    DispatchQueue.global().async {
        [weak self] in
        self?.operationQueue.addOperations(operations, waitUntilFinished: true)
        DispatchQueue.main.async {
            self?.activityIndicator.stopAnimating()
        }
    }
    
}

最后给一下源代码的下载地址:
终于把源代码放在了gitHub上。有钱的大爷就点击下方打赏点卖笑钱,有力气的就在github上给个星星✨。gitHub
么么哒~(~o ̄3 ̄)~ 爱你们~


iOS多线程系列之一:Operation基础操作,按优先级加载图片
iOS多线程系列之二:Operation实例,异步加载CollectionView图片
iOS多线程系列之三:使用GCD实现异步下载图片
iOS多线程系列之四:GCD进阶,单例、信号量、任务组
iOS多线程系列之五:使用Thread进行多线程间通讯,协调子线程任务

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容