现如今,几乎我们的app都需要和服务端进行网络通信。但是,如果我们像处理用户交互一样处理网络通信,整个UI就很容易被卡住
对于每一个app来说,都有一个主线程用来处理界面更新、响应用户交互等事件,这些事件发生时,它们就会被放到一个由主线程处理的事件队列里由主线程按照发生的顺序依次处理它们。
当主线程进行下载图片(或者进行耗时操作的时候)处理时候,其他的UI交互事件只能暂时等在队列里。因此,这个时候UI是没有响应的。而当下载进行完成后,主线程发现还有一个UI拖动的事件,这时,他就会把处理UI移动到相应的位置。
当然,这种体验是非常糟糕的,因此,我们不要把任何网络IO的代码放到一个app的主线程中队列执行:
创建并使用额外的Serial Queue
一个serial queue和主线程使用的队列是非常类似的:
- 它们都只能顺序执行队列中的任务;
- 主线程队列可以和多个serial queue并行执行
因此,我们只要新创建一个serial queue,然后在进行耗时任务时,把耗时任务放进去就可以了。我们使用下面的方式创建一个自己的serial queue:
// Create a serial queue
let serialQueue = dispatch_queue_create("serial queue name", DISPATCH_QUEUE_SERIAL)
创建好之后,我们使用dispatch_async,把任务添加到serial queue
dispatch_async(serialQueue, {
let img1 = Downloader.downloadImageWithURL(self.imageUrls[0])
// 所有更新UI的操作,必须在主线程中完成
// 我们使用dispatch_get_main_queue获得主线程队列
dispatch_async(dispatch_get_main_queue(), {
self.image1.image = img1
self.image1.clipsToBounds = true
})
})
dispatch_async的第一个参数是要插入的队列,第二个参数是一个()->Void类型的closure,表示要插入到队列的任务。
使用多个serial queue
我们还可以把图片放在不同的serial queue里下载,这样,虽然在一个serial queue内部任务是串行执行的,但多个serial queue之间,是并行执行的。
// Add task
let serialQueue1 =
dispatch_queue_create("name",
DISPATCH_QUEUE_SERIAL)
dispatch_async(serialQueue1, {
let img1 =
Downloader.downloadImageWithURL(self.imageUrls[0])
dispatch_async(dispatch_get_main_queue(), {
self.image1.image = img1
self.image1.clipsToBounds = true
})
// Omit for simplicity...
})
let serialQueue2 =
dispatch_queue_create("name1",
DISPATCH_QUEUE_SERIAL)
dispatch_async(serialQueue2, {
let img2 =
Downloader.downloadImageWithURL(self.imageUrls[1])
dispatch_async(dispatch_get_main_queue(), {
self.image2.image = img2
self.image2.clipsToBounds = true
})
// Omit for simplicity...
})
我们向大家介绍了通过serial queue和主线程并行执行任务以及在主线程中更新UI的方法。
我们介绍了serial,在iOS中叫做Grand Central Dispatch,它是一组相对低层的C语言API。尽管GCD对线程管理进行了封装,如果我们要管理队列中的任务(例如:查看任务状态、取消任务、控制任务之间的执行顺序等)仍然不是很方便。
网友评论