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一旦运行,默认就会开启一条线程。这条线程,我们通常称作为“主线程”
主线程的作用:
- 刷新UI
- 处理UI事件,例如点击、滚动、拖拽。
如果主线程的操作太多、太耗时,就会造成App卡顿现象严重。所以,通常我们都会把耗时的操作放在子线程中进行,获取到结果之后,回到主线程去刷新UI。
2. Operation
我们来看看基础使用:
1 // 最基本的使用Operation 2 private func basicOperation() { 3 // 第一步:创建Operation 4 let op = Operation.init() 5 // 第二步:把要执行的代码放入operation中 6 op.completionBlock = { 7 8 print(#function,#line,Thread.current) 9 } 10 // 第三步:创建OperationQueue 11 let opQueue = OperationQueue.init() 12 // 第四步:把Operation加入到线程中 13 opQueue.addOperation(op) 14 }
使用BlockOperation创建operatoin,并直接运行。咱们看看会在哪条线程执行。
1 //创建一个简单的BlockOperation 2 private func CreatBasicBlockOperation() { 3 //使用BlockOperation创建operation 4 let operation = BlockOperation.init { 5 //打印,看看在哪个线程中 6 7 print(#function,#line,Thread.current) 8 } 9 10 //直接运行operation,看看运行在哪个线程中 11 operation.start() 12 }
打印一下看看运行的结果:
这就是我们说,创建完Operation如果直接运行,就会在当前线程执行。也就是说,如果实在主线程创建并且start的,那就会在主线程执行;如果是在子线程创建并且start的,那就会在子线程执行。
3. Basic Demo
在这个例子里面,需求如下:
1,在子线程加载每个图片的数据
2,图片数据下载完毕之后,显示出来
3,开始请求数据的时候,让指示符开始转动
4,所有图片下载完毕后,指示符停止转动
3. Basic Demo
在这个例子里面,需求如下:
1,在子线程加载每个图片的数据
2,图片数据下载完毕之后,显示出来
3,开始请求数据的时候,让指示符开始转动
4,所有图片下载完毕后,指示符停止转动
3.2 Swift中的do catch
这个是Swift和OC不一样的地方。Swift中出现了可选值这么一个东西,这个不是这次的重点。想深入了解的童鞋可以参看这篇:?和 !的使用
Swift 里有四种方法来处理错误:
- 把错误从函数传递到调用函数的代码里
- 使用一个 do-catch 语句来处理错误
- 把错误当做一个可选值来处理
- 断言这个错误不会发生
因为Demo里面用到了do catch,那咱们就只说do catch.
在Swift的标准try中,是要配合do catch的。
下面是do-catch语句的一般格式,如果do分句内的代码抛出了一个错误,它就被catch分句捕获,并判断由哪个分句来处理此错误。
1 do { 2 try expression 3 statements 4 } catch pattern 1 { 5 statements 6 } catch pattern 2 where condition { 7 statements 8 }
3.3 优先级
在思维导图里面出现了两个优先级。一个是属于Operation 的,一个是属于OperationQueue的。那咱们分看看看这两个都是啥。
3.3.1 Operation中的优先级
Operation里面的这个叫做qualityOfService
1 public enum QualityOfService : Int { 2 case userInteractive 3 case userInitiated 4 case utility 5 case background 6 case `default` 7 }
1 userInteractive:最高优先级,用于用户交互事件 2 userInitiated :次高优先级,用于用户需要马上执行的事件 3 utility:普通优先级,用于普通任务 4 background:最低优先级,用于不重要的任务 5 default:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
3.3.2 operationQueue 里面的优先级
operationQueue中表示优先级的属性是queuePriority
,表示操作在队列中的优先级。
1 public enum QueuePriority : Int { 2 case veryLow 3 case low 4 case normal 5 case high 6 case veryHigh 7 }
这些优先级都是相对的,并不是是说必须要执行完最高的才执行次重要的。这里面并没有一个特别严格顺序。只是在分配资源上有倾向性。如果队列需要有严格的执行顺序,还是要添加依赖关系的,这个是我们下一篇文章要分享的内容。
4. 案例实现
Operation 基本应用及优先级小案例。
实现后效果如下:
这个Demo里面,用了两种方法创建Operation。
在startBasicDemo
,使用的是闭包创建Operation的方式。在startPriorityDemo
里面使用的是自定义的构造方法创建的Operation,然后把任务数组加入到线程中。
代码:
1 // Operation 案例 2 fileprivate func startBasicDemo() 3 { 4 let operationQueue = OperationQueue.init() 5 operationQueue.maxConcurrentOperationCount = 3 6 7 let activityIndicator = UIActivityIndicatorView() 8 activityIndicator.startAnimating() 9 10 let a = UIImageView(frame: CGRect(x: 0, y: 64, 414, height: 100)) 11 self.view.addSubview(a) 12 let b = UIImageView(frame: CGRect(x: 0, y: 164, 414, height: 100)) 13 self.view.addSubview(b) 14 let c = UIImageView(frame: CGRect(x: 0, y: 264, 414, height: 100)) 15 self.view.addSubview(c) 16 let d = UIImageView(frame: CGRect(x: 0, y: 364, 414, height: 100)) 17 self.view.addSubview(d) 18 19 20 let imageViews = [a, b, c, d] 21 for imageView in imageViews { 22 if let url = URL(string: "https://placebeard.it/355/140") 23 { 24 do { 25 let image = UIImage(data:try Data(contentsOf: url)) 26 27 DispatchQueue.main.async 28 { 29 imageView.image = image 30 } 31 }catch 32 { 33 print(error) 34 } 35 } 36 } 37 // global queue 38 DispatchQueue.global().async 39 { 40 [weak self] in 41 // 等待所有操作都完成了,回到主线程停止刷新器。 42 // wait Until All Operations are finished, then stop animation of activity indicator 43 operationQueue.waitUntilAllOperationsAreFinished() 44 DispatchQueue.main.async 45 { 46 activityIndicator.stopAnimating() 47 } 48 49 } 50 }
设置了优先级的Demo:
1 fileprivate func startPriorityDemo() { 2 operationQueue.maxConcurrentOperationCount = 2 3 activityIndicator.startAnimating() 4 5 var operations = [Operation]() 6 for (index, imageView) in (imageViews?.enumerated())! { 7 if let url = URL(string: "https://placebeard.it/355/140") { 8 // 使用构造方法创建operation 9 let operation = convenienceOperation(setImageView: imageView, withURL: url) 10 11 //根据索引设置优先级 12 switch index { 13 case 0: 14 operation.queuePriority = .veryHigh 15 case 1: 16 operation.queuePriority = .high 17 case 2: 18 operation.queuePriority = .normal 19 case 3: 20 operation.queuePriority = .low 21 default: 22 operation.queuePriority = .veryLow 23 } 24 25 operations.append(operation) 26 } 27 } 28 29 // 把任务数组加入到线程中 30 DispatchQueue.global().async { 31 [weak self] in 32 self?.operationQueue.addOperations(operations, waitUntilFinished: true) 33 DispatchQueue.main.async { 34 self?.activityIndicator.stopAnimating() 35 } 36 } 37 38 }