关于多线程处理,很多人不敢用,因为多线程常常会导致一些奇葩的问题出现(大多都是资源共享导致的)。但存在肯定是有其道理的,如果你对它足够了解,当然也不一定就不出问题。起码是出了问题很快就可以定位到。至于多线程的优势,我想不说大家也都知道,肯定是比单线程要快的。这里就讲讲iOS的多线程并发处理。现在的设备很少是单核处理器了,如果你还用单线程,那可真是暴殄天物啊。那么多资源就被你这样浪费了。在iOS中主线程是处理UI的,你如果什么都在主线程里操作,这样用户的体验会非常的差,因为操作一个事件就要等其结束才能继续。所以用户体验很差,今天就来谈谈iOS的多线程处理。
第一部分:GCD
GCD是管理多线程最常用的api,GCD提供并管理任务队列。当然首先要了解队列是什么东西。
队列是管理对象的先进先出(FIFO)的一种数据结构,就像排队买票,排在前面的先买,后面的后买一样。
- DISPATCH_QUEUE_PRIORITY_HIGH
- DISPATCH_QUEUE_PRIORITY_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW
- DISPATCH_QUEUE_PRIORITY_BACKGROUND
- 当然,他们的优先级从上到下越来越低的是。你可以使用任意一个你想用的队列,但是请注意,这些队列是系统提供给你的,所以队列里面的任务不一定都是你的任务(可能有其他的任务)。
<span style="font-size:14px;">@IBActionfuncdidClickOnStart(sender:AnyObject){
letimg1=Downloader.downloadImageWithURL(imageURLs[0])
self.imageView1.image=img1
letimg2=Downloader.downloadImageWithURL(imageURLs[1])
self.imageView2.image=img2
letimg3=Downloader.downloadImageWithURL(imageURLs[2])
self.imageView3.image=img3
letimg4=Downloader.downloadImageWithURL(imageURLs[3])
self.imageView4.image=img4
}</span>
<span style="font-size:14px;">letqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
dispatch_async(queue){()->Voidin
letimg1=Downloader.downloadImageWithURL(imageURLs[0])
dispatch_async(dispatch_get_main_queue(),{
self.imageView1.image=img1
})
}</span>
<span style="font-size:14px;">@IBActionfuncdidClickOnStart(sender:AnyObject){
letqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
dispatch_async(queue){()->Voidin
letimg1=Downloader.downloadImageWithURL(imageURLs[0])
dispatch_async(dispatch_get_main_queue(),{
self.imageView1.image=img1
})
}
dispatch_async(queue){()->Voidin
letimg2=Downloader.downloadImageWithURL(imageURLs[1])
dispatch_async(dispatch_get_main_queue(),{
self.imageView2.image=img2
})
}
dispatch_async(queue){()->Voidin
letimg3=Downloader.downloadImageWithURL(imageURLs[2])
dispatch_async(dispatch_get_main_queue(),{
self.imageView3.image=img3
})
}
dispatch_async(queue){()->Voidin
letimg4=Downloader.downloadImageWithURL(imageURLs[3])
dispatch_async(dispatch_get_main_queue(),{
self.imageView4.image=img4
})
}
}</span>
<span style="font-size:14px;">@IBActionfuncdidClickOnStart(sender:AnyObject){
letserialQueue=dispatch_queue_create("com.appcoda.imagesQueue",DISPATCH_QUEUE_SERIAL)
dispatch_async(serialQueue){()->Voidin
letimg1=Downloader.downloadImageWithURL(imageURLs[0])
dispatch_async(dispatch_get_main_queue(),{
self.imageView1.image=img1
})
}
dispatch_async(serialQueue){()->Voidin
letimg2=Downloader.downloadImageWithURL(imageURLs[1])
dispatch_async(dispatch_get_main_queue(),{
self.imageView2.image=img2
})
}
dispatch_async(serialQueue){()->Voidin
letimg3=Downloader.downloadImageWithURL(imageURLs[2])
dispatch_async(dispatch_get_main_queue(),{
self.imageView3.image=img3
})
}
dispatch_async(serialQueue){()->Voidin
letimg4=Downloader.downloadImageWithURL(imageURLs[3])
dispatch_async(dispatch_get_main_queue(),{
self.imageView4.image=img4
})
}
}</span>
NSOperation
1、支持依赖
2、改变执行的优先级
<span style="font-size:14px;">publicenumNSOperationQueuePriority: Int{
caseVeryLow
caseLow
caseNormal
caseHigh
caseVeryHigh
}</span>
3、对于任何给定的队列,你可以取消操作,当任务被加到队列中后,你可以调用NSOperation实例对象的cancel()方法来取消这个任务。但取消操作有如下三种情形可能:
任务已经执行完,取消则无效果。
任务正在被执行,如果那样,系统不会强制停止任务,但取消的属性(cancelled)将被置为真。
任务在队列等待执行,那么它将被取消。
4、NSOperation有三个有用的布尔值属性finished, cancelled, and ready。finished 将被置为真当任务完成的时候,cancelled将为真当任务被取消的时候,ready将被置为真,当其可以被执行的时候。
5、任何的NSOperation都有一个选项设置完成后的block,当任务被完成后调用,也就是当finished属性被赋值为真之后,block开始执行。
接下来我们重写上面的demo用NSOperationQueues,首先声明变量在ViewController中
varqueue=NSOperationQueue()
然后重写这个方法didClickOnStart
<span style="font-size:14px;">@IBActionfuncdidClickOnStart(sender:AnyObject){
queue=NSOperationQueue()
queue.addOperationWithBlock{()->Voidin
letimg1=Downloader.downloadImageWithURL(imageURLs[0])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView1.image=img1
})
}
queue.addOperationWithBlock{()->Voidin
letimg2=Downloader.downloadImageWithURL(imageURLs[1])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView2.image=img2
})
}
queue.addOperationWithBlock{()->Voidin
letimg3=Downloader.downloadImageWithURL(imageURLs[2])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView3.image=img3
})
}
queue.addOperationWithBlock{()->Voidin
letimg4=Downloader.downloadImageWithURL(imageURLs[3])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView4.image=img4
})
}
}</span>
上面用了addOperationWithBlock这个方法,接下来我们加入了回调block重新处理下
<span style="font-size:14px;">@IBAction func didClickOnStart(sender: AnyObject) {
queue = NSOperationQueue()
let operation1 = NSBlockOperation(block: {
let img1 = Downloader.downloadImageWithURL(imageURLs[0])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView1.image = img1
})
})
operation1.completionBlock = {
print("Operation 1 completed")
}
queue.addOperation(operation1)
let operation2 = NSBlockOperation(block: {
let img2 = Downloader.downloadImageWithURL(imageURLs[1])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView2.image = img2
})
})
operation2.completionBlock = {
print("Operation 2 completed")
}
queue.addOperation(operation2)
let operation3 = NSBlockOperation(block: {
let img3 = Downloader.downloadImageWithURL(imageURLs[2])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView3.image = img3
})
})
operation3.completionBlock = {
print("Operation 3 completed")
}
queue.addOperation(operation3)
let operation4 = NSBlockOperation(block: {
let img4 = Downloader.downloadImageWithURL(imageURLs[3])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView4.image = img4
})
})
operation4.completionBlock = {
print("Operation 4 completed")
}
queue.addOperation(operation4)
}</span>
取消的操作为了验证取消操作,我们在导航栏上加了取消按钮。假设我们有四个任务,#3依赖#2,#2依赖任务#1,#4没有依赖。
下面是取消部分代码
<span style="font-size:14px;">@IBAction func didClickOnCancel(sender: AnyObject) {
self.queue.cancelAllOperations()
}</span>
这个需要关联取消按钮的,并且在这个方法didClickOnStart中加入如下两行代码:
<span style="font-size:14px;">operation2.addDependency(operation1)
operation3.addDependency(operation2)</span>
然后改变回调方法,加上日志
<span style="font-size:14px;">operation1.completionBlock = {
print("Operation 1 completed, cancelled:\(operation1.cancelled) ")
}</span>
其他几个类似这样的。
点开始按钮之后,再迅速按取消,结果如下:
#1已经开始了,所以取消没有任何结果,但#2和#3都依赖于#1,所以#2和#3都被取消了,但#4没有任何关联,则和#1一起执行了。效果图如下:
水平有限,有机会再回来修改。