GCD(Grand Central Dispatch)
如果我们有一件工作,想要在某条指定的thread上执行,现在最简单的方法大概就是调用GCD。GCD其实包含相当多的API,是一群C function的组合,其中,我们最常用的是dispatch_async
。
dispatch_async
dispatch_async
这个function,可以让我们选择要在哪个指定的thread 上,用非同步的方式执行一个block。比方说,我们现在在前景,但是想要在背景执行一件工作,就会这么写:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[someObject doSomething];
});
dispatch_get_global_queue
这个function 会让系统根据目前的状况,在适当时机建立一条thread,第一个参数是这条thread 执行工作的优先程度,优先程度会从2 到-2 安排,2 为最重要,-2 为最不重要;至于第二个参数则是保留参数,目前都没有作用,直接填0 即可。
如果我们已经在背景了,想要在main thread执行工作,那么,就把dispatch_get_global_queue
换成dispatch_get_main_queue
dispatch_async(dispatch_get_main_queue(), ^{
[someObject doSomethingHere];
});
我们经常会先让某个工作在背景执行,执行完毕之后,再继续在main thread 更新UI,让用户知道这件工作已经执行完毕,我们便可以组合前面两个 调用:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[someObject doSomethingInBackground];
dispatch_async(dispatch_get_main_queue(), ^{
[someObject doSomethingOnMainThread];
});
});
如果我们想要让好几件工作都在背景执行,而每件工作并非平行执行,而是一件工作做完之后,再继续下一件,我们便可以使用serial 的queue。像这样:
dispatch_queue_t serialQueue = \
dispatch_queue_create("com.kkbox.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
[someObject doSomethingHere];
});
dispatch_async(serialQueue, ^{
[someObject doSomethingHereAsWell];
});
dispatch_sync
不同于dispatch_async
会做平行处理,呼叫dispatch_sync
的时候,则是会先把dispatch_sync
的这个block做完之后,才继续执行到程式的下一行。
我们在呼叫dispatch_sync
的时候要特别注意:如果我们已经在某一条thread中,而调用dispatch_sync
时所传入的thread就是目前所在的thread,那么会造成程式执行时卡死。比方说,我们已经在main thread了,但我们却呼叫:
dispatch_sync(dispatch_get_main_queue(), ^{
[someObject doSomethingHere];
});
这段程式就会卡住。我们可以用NSThread的一些method检查我们目前正在哪条thread,例如使用+isMainThread
检查是否是main thread。
其他一些好用的API
和dispatch_async
与`dispatch_sync
相较,底下这些API会比较少用,但是可以解决不少麻烦的问题。
dispatch_once
dispatch_once
保证某个block只会被执行一次,现在大家最常使用这个特性实作singleton。我们在「再谈Singleton」这一章当中也提过。
dispatch_after
可以延后执行某个block 在某个指定的dispatch queue 上执行,我们可以用这个function 代替timer。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[someObject doSomething];
});
dispatch_apply
如果我们想要重复执行某个block,就可以考虑使用dispatch_apply
。dispatch_apply
有三个参数,第一个参数是要执行的次数,第二个参数则是要在哪个dispatch queue上执行:就像前面提到的,如果想要平行执行,就调用dispatch_get_global_queue
,如果想要依序执行,就建立一个serial的dispatch queue。