通常主线程和其他线程的使用场景
主线程一般用于
绘制UI
响应用户操作等
其他线程用于
网络请求
解析网络返回等
Tips: 解压、打开 Zip 包,读写较大文件的操作也不宜放在主线程里。
一般异步网络请求中会有一个 completionBlock ,这个 completionBlock 是在主线程中被调用的。
所以,可能消耗大量时间的代码(例如上面提到的处理 Zip 包的方法)也不宜放在这些 block 中。如下面的代码所示:
[request setCompletionBlock:^{
NSLog(@"Zip file downloaded.");
NSData *data = [request responseData];
[self processZip:data sourceURL:sourceURL]; // Ack - heavy work on main thread!
}];
那么,如果在blcok 中有数据需要花费大量时间处理,我们可以使用 Grand Central Dispatch(GCD)系统,让数据在后台被处理,或者使用GCD在后台执行这些代码。简单来说,如果希望有代码在后台执行,只需要调用 dispatch_async,然后把代码扔进去。
接来下的事就交给 GCD 啦,如果需要,GCD 会创建一个新的线程;或者 GCD 会重用一个已经存在的可用线程。
当你调用 dispatch_async 的时候,就是将代码传入了一个 dispatch 队列,这个队列里存储了所有你传入的 block。
我们可以创建自己的 dispatch 队列(通过 dispatch create 方法),也可以为了主线程的到一个特殊的队列(通过 dispatchgetmainqueue)。
一个 dispatch 队列(queue)是按次序排设好的,这就意味着队列中每次只有一个 block 的代码被执行。这个特性非常方便,我们可以用它保护共享数据(shared data)。
关于保护数据的核心思想是:你需要设置好你的代码,使得一个特定的数据结构只能被一个特定的正在运行的 dispatch 队列所访问。因为 dispatch 队列按次序执行 block,那么每次就只有一个block 能够访问该数据结构。
GCD 实践
预先声明一个dispatch_queue,添加一个 dispatch queue 实例
// Add new instance variable
dispatch_queue_t backgroundQueue;
关于 dispatch_queue_t 苹果官方文档说明:一个 dispatch queue 是一个用来注册将要被按顺序执行的代码块的轻量级对象。
创建dispatch queue
backgroundQueue = dispatch_queue_create("com.razeware.imagegrabber.bgqueue", NULL);
初始化前面说声明的 dispatch queue,并给这个dispatch queue 命名。上面填 null 参数的地方还可以填 DISPATCHQUEUESERIAL(等同于null)或者 DISPATCHQUEUECONCURRENT。
DISPATCHQUEUESERIAL :按先进先出原则执行 block 的 dispatch 队列。
DISPATCHQUEUECONCURRENT:执行当前 block 的 dispatch 队列。虽然该队列执行的是当前的代码,我们也可以使用 barrier block 在队列中创建同步点。
使用刚才创建的 dispatch queue
(void)process {
dispatch_async(backgroundQueue, ^(void) { [self processHtml]; });
}
原本 – (void)process 中的代码是直接执行 [self processHtml]; processHTMl 方法阻塞了主线程,而现在通过调用 dispatch_async 我们可以使得 processHtml 在我们创建的 backgroundQueue 中在后台运行。
关于NSOperations 和 operation 序列
NSOperations 其实就是基于 GCD 实现的,使用NSOperation 的时候其实也就是在使用 GCD。但是NSOperation 给我们提供了更多更方便的功能,我们可以操作一些 operation,这些operation 依赖于其他 operation。还可以在 submit block 之后重新对 operation queue 进行排序。
Bingo!
之后应该会总结一些关于 GCD 使用的更详细的文章。