1.GCD获取主队列即main_queue,同步串行执行(主队列是串行,即有顺序的执行任务),可能会造成死锁;最常见的就是,在某个方法中,同步串行等待方法结束执行穿行方法,然后这个方法又在等待穿行block执行完毕。。互相等待造成死锁。
2.DISPATCH_GET_GLOBAL_QUEUE获取到的是一个并发队列,这个而且这个方法获取到的全局队列第一个参数表示优先级,默认为0,第二个参数备用,为扩展参数.
dispatch_get_gloabal_queue 的第一个参数为枚举类型(默认为0),决定任务的优先级 ,第二个参数为Apple保留参数,传0
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN(后台任务,优先级最低)
3.异步线程,完成耗时操作,返回主线程执行ui刷新等方法,可以这么操作:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: kLatestKivaLoansURL];
[self performSelectorOnMainThread:@selector(fetchedData:) withObject:datawaitUntilDone:YES];
});
}
4.app运行的时候,每个程序系统自动提供了3个Concurrent Queues
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t aHQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t aLQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
啥意思一看就明白,3个优先级别的concurrent queues
5.dispatch_block_t
是被提交到调度队列的代码块的原型,而该代码块没有参数和返回值,声明如下:
typedef void (^dispatch_block_t)(void);
在之前我们介绍过NSOperation的一些东西,这次我们来聊一聊另一个iOS开发最经常使用的技术之一 --- GCD,GCD将线程的管理移到系统级别,你只需要定义好要执行的任务,然后丢到合适的Dispatch queue,GCD会负责创建线程来执行你的代码,由于这部分是处于系统级别,所以执行的性能通常非常高。GCD这部分代码苹果已开源,有兴趣的可以去下载了解一下: 地址 在介绍GCD之前我们先了解一下Quality of Service:Quality of Service(QoS)
这是在iOS8之后提供的新功能,苹果提供了几个Quality of Service枚举来使用:user interactive, user initiated, utility 和 background,通过这告诉系统我们在进行什么样的工作,然后系统会通过合理的资源控制来最高效的执行任务代码,其中主要涉及到CPU调度的优先级、IO优先级、任务运行在哪个线程以及运行的顺序等等,我们通过一个抽象的Quality of Service参数来表明任务的意图以及类别。
- NSQualityOfServiceUserInteractive 与用户交互的任务,这些任务通常跟UI级别的刷新相关,比如动画,这些任务需要在一瞬间完成
- NSQualityOfServiceUserInitiated 由用户发起的并且需要立即得到结果的任务,比如滑动scroll view时去加载数据用于后续cell的显示,这些任务通常跟后续的用户交互相关,在几秒或者更短的时间内完成
- NSQualityOfServiceUtility 一些可能需要花点时间的任务,这些任务不需要马上返回结果,比如下载的任务,这些任务可能花费几秒或者几分钟的时间
- NSQualityOfServiceBackground 这些任务对用户不可见,比如后台进行备份的操作,这些任务可能需要较长的时间,几分钟甚至几个小时
- NSQualityOfServiceDefault 优先级介于user-initiated 和 utility,当没有 QoS信息时默认使用,开发者不应该使用这个值来设置自己的任务
Qos可以跟GCD queue做个对照:
下面我们了解一下GCD的一些用法:
Dispatch Queue
开发者将需要执行的任务添加到合适的Dispatch Queue中即可,Dispatch Queue会根据任务添加的顺序先到先执行,其中有以下几种队列:
- main dispatch queue 功能跟主线程一样,通过dispatch_get_main_queue()来获取,提交到main queue的任务实际上都是在主线程执行的,所以这是一个串行队列
- global dispatch queues 系统给每个应用提供四个全局的并发队列,这四个队列分别有不同的优先级:高、默认、低以及后台,用户不能去创建全局队列,只能根据优先级去获取:
dispatch_queue_t queue ; queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- user create queue
用户可以通过dispatch_queue_create自己创建队列,该函数有两个参数,第一个是队列的名称,在debug的时候方便区分;第二个是队列的一些属性,NULL或者DISPATCH_QUEUE_SERIAL创建出来的队列是串行队列,如果传递DISPATCH_QUEUE_CONCURRENT则为并行队列。//创建并行队列 dispatch_queue_t queue; queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_CONCURRENT);
- 队列优先级
dispatch_queue_create创建队列的优先级跟global dispatch queue的默认优先级一样,假如我们需要设置队列的优先级,可以通过dispatch_queue_attr_make_with_qos_class或者dispatch_set_target_queue方法;
//指定队列的QoS类别为QOS_CLASS_UTILITY dispatch_queue_attr_t queue_attr = dispatch_queue_attr_make_with_qos_class (DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY,-1); dispatch_queue_t queue = dispatch_queue_create("queue", queue_attr);
dispatch_set_target_queue的第一个参数为要设置优先级的queue,第二个参数是对应的优先级参照物
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.MyQueue",NULL); dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0); //serialQueue现在的优先级跟globalQueue的优先级一样 dispatch_set_target_queue(serialQueue, globalQueue);
- dispatch_set_target_queue
dispatch_set_target_queue除了能用来设置队列的优先级之外,还能够创建队列的层次体系,当我们想让不同队列中的任务同步的执行时,我们可以创建一个串行队列,然后将这些队列的target指向新创建的队列即可,比如dispatch_queue_t targetQueue = dispatch_queue_create("target_queue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT); dispatch_set_target_queue(queue1, targetQueue); dispatch_set_target_queue(queue2, targetQueue); dispatch_async(queue1, ^{ NSLog(@"do job1"); [NSThread sleepForTimeInterval:3.f]; }); dispatch_async(queue2, ^{ NSLog(@"do job2"); [NSThread sleepForTimeInterval:2.f]; }); dispatch_async(queue2, ^{ NSLog(@"do job3"); [NSThread sleepForTimeInterval:1.f]; });
可以看到执行的结果如下,这些队列会同步的执行任务。
GCDTests[13323:569147] do job1 GCDTests[13323:569147] do job2 GCDTests[13323:569147] do job3
- dispatch_barrier_async
dispatch_barrier_async用于等待前面的任务执行完毕后自己才执行,而它后面的任务需等待它完成之后才执行。一个典型的例子就是数据的读写,通常为了防止文件读写导致冲突,我们会创建一个串行的队列,所有的文件操作都是通过这个队列来执行,比如FMDB,这样就可以避免读写冲突。不过其实这样效率是有提升的空间的,当没有更新数据时,读操作其实是可以并行进行的,而写操作需要串行的执行,如何实现呢:dispatch_queue_t queue = dispatch_queue_create("Database_Queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"reading data1"); }); dispatch_async(queue, ^{ NSLog(@"reading data2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"writing data1"); [NSThread sleepForTimeInterval:1]; }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@"reading data3"); });
执行结果如下:
GCDTests[13360:584316] reading data2 GCDTests[13360:584317] reading data1 GCDTests[13360:584317] writing data1 GCDTests[13360:584317] reading data3
我们将写数据的操作放在dispatch_barrier_async中,这样能确保在写数据的时候会等待前面的读操作完成,而后续的读操作也会等到写操作完成后才能继续执行,提高文件读写的执行效率。
- dispatch_queue_set_specific 、dispatch_get_specific
这两个API类似于objc_setAssociatedObject跟objc_getAssociatedObject,FMDB里就用到这个来防止死锁,来看看FMDB的部分源码
static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey; //创建一个串行队列来执行数据库的所有操作 _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); //通过key标示队列,设置context为self dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
当要执行数据库操作时,如果在queue里面的block执行过程中,又调用了 indatabase方法,需要检查是不是同一个queue,因为同一个queue的话会产生死锁情况
- (void)inDatabase:(void (^)(FMDatabase *db))block { FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey); assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"); }
- dispatch_apply
dispatch_apply类似一个for循环,会在指定的dispatch queue中运行block任务n次,如果队列是并发队列,则会并发执行block任务,dispatch_apply是一个同步调用,block任务执行n次后才返回。
简单的使用方法:dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT); //并发的运行一个block任务5次 dispatch_apply(5, queue, ^(size_t i) { NSLog(@"do a job %zu times",i+1); }); NSLog(@"go on");
输出结果:
GCDTests[10029:760640] do a job 2 times GCDTests[10029:760640] do a job 1 times GCDTests[10029:760640] do a job 3 times GCDTests[10029:760640] do a job 5 times GCDTests[10029:760640] do a job 4 times GCDTests[10029:760640] go on
在某些场景下使用dispatch_apply会对性能有很大的提升,比如你的代码需要以每个像素为基准来处理计算image图片。同时dispatch apply能够避免一些线程爆炸的情况发生(创建很多线程)
//危险,可能导致线程爆炸以及死锁 for (int i = 0; i < 999; i++){ dispatch_async(q, ^{...}); } dispatch_barrier_sync(q, ^{}); // 较优选择, GCD 会管理并发 dispatch_apply(999, q, ^(size_t i){...});
Dispatch Block
添加到gcd队列中执行的任务是以block的形式添加的,block封装了需要执行功能,block带来的开发效率提升就不说了,gcd跟block可以说是一对好基友,能够很好的配合使用。
- 创建block
我们可以自己创建block并添加到queue中去执行dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); //创建block dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"do something"); }); dispatch_async(queue, block);
在创建block的时候我们也可以通过设置QoS,指定block对应的优先级,在dispatch_block_create_with_qos_class中指定QoS类别即可:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); dispatch_block_t block = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{ NSLog(@"do something with QoS"); }); dispatch_async(queue, block);
- dispatch_block_wait
当需要等待前面的任务执行完毕时,我们可以使用dispatch_block_wait这个接口,设置等待时间DISPATCH_TIME_FOREVER会一直等待直到前面的任务完成:dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"before sleep"); [NSThread sleepForTimeInterval:1]; NSLog(@"after sleep"); }); dispatch_async(queue, block); //等待前面的任务执行完毕 dispatch_block_wait(block, DISPATCH_TIME_FOREVER); NSLog(@"coutinue");
程序运行结果:
GCDTests[16679:863641] before sleep GCDTests[16679:863641] after sleep GCDTests[16679:863529] coutinue
- dispatch_block_notify
dispatch_block_notify当观察的某个block执行结束之后立刻通知提交另一特定的block到指定的queue中执行,该函数有三个参数,第一参数是需要观察的block,第二个参数是被通知block提交执行的queue,第三参数是当需要被通知执行的block,函数的原型:void dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue, dispatch_block_t notification_block);
具体使用的方法:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); dispatch_block_t previousBlock = dispatch_block_create(0, ^{ NSLog(@"previousBlock begin"); [NSThread sleepForTimeInterval:1]; NSLog(@"previousBlock done"); }); dispatch_async(queue, previousBlock); dispatch_block_t notifyBlock = dispatch_block_create(0, ^{ NSLog(@"notifyBlock"); }); //当previousBlock执行完毕后,提交notifyBlock到global queue中执行 dispatch_block_notify(previousBlock, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), notifyBlock);
运行结果:
GCDTests[17129:895673] previousBlock begin GCDTests[17129:895673] previousBlock done GCDTests[17129:895673] notifyBlock
- dispatch_block_cancel
之前在介绍nsopreration的时候提到它的一个优点是可以取消某个operation,现在在iOS8之后,提交到gcd队列中的dispatch block也可取消了,只需要简单的调用dispatch_block_cancel传入想要取消的block即可:dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); dispatch_block_t block1 = dispatch_block_create(0, ^{ NSLog(@"block1 begin"); [NSThread sleepForTimeInterval:1]; NSLog(@"block1 done"); }); dispatch_block_t block2 = dispatch_block_create(0, ^{ NSLog(@"block2 "); }); dispatch_async(queue, block1); dispatch_async(queue, block2); dispatch_block_cancel(block2);
可以看到如下的执行结果,block2不再执行了。
GCDTests[17271:902981] block1 begin GCDTests[17271:902981] block1 done
Dispatch Group
当我们想在gcd queue中所有的任务执行完毕之后做些特定事情的时候,也就是队列的同步问题,如果队列是串行的话,那将该操作最后添加到队列中即可,但如果队列是并行队列的话,这时候就可以利用dispatch_group来实现了,dispatch_group能很方便的解决同步的问题。dispatch_group_create可以创建一个group对象,然后可以添加block到该组里面,下面看下它的一些用法:
- dispatch_group_wait
dispatch_group_wait会同步地等待group中所有的block执行完毕后才继续执行,类似于dispatch barrierdispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); //将任务异步地添加到group中去执行 dispatch_group_async(group,queue,^{ NSLog(@"block1"); }); dispatch_group_async(group,queue,^{ NSLog(@"block2"); }); dispatch_group_wait(group,DISPATCH_TIME_FOREVER); NSLog(@"go on");
执行结果如下,只有block1跟block2执行完毕后才会执行dispatch_group_wait后面的内容。
GCDTests[954:41031] block2 GCDTests[954:41032] block1 GCDTests[954:40847] go on
- dispatch_group_notify
功能与dispatch_group_wait类似,不过该过程是异步的,不会阻塞该线程,dispatch_group_notify有三个参数void dispatch_group_notify(dispatch_group_t group, //要观察的group dispatch_queue_t queue, //block执行的队列 dispatch_block_t block); //当group中所有任务执行完毕之后要执行的block
简单的示意用法:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group,queue,^{ NSLog(@"block1"); }); dispatch_group_async(group,queue,^{ NSLog(@"block2"); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"done"); }); NSLog(@"go on");
可以看到如下的执行结果
GCDTests[1046:45104] go on GCDTests[1046:45153] block1 GCDTests[1046:45152] block2 GCDTests[1046:45104] done
- dispatch_group_enter dispatch_group_leave
假如我们不想使用dispatch_group_async异步的将任务丢到group中去执行,这时候就需要用到dispatch_group_enter跟dispatch_group_leave方法,这两个方法要配对出现,以下这两种方法是等价的:dispatch_group_async(group, queue, ^{ });
等价于
dispatch_group_enter(group); dispatch_async(queue, ^{ dispatch_group_leave(group); });
简单的使用方法,可以自己试试没有写dispatch_group_leave会发生什么。
dispatch_group_t group = dispatch_group_create(); for (int i =0 ; i<3; i++) { dispatch_group_enter(group); NSLog(@"do block:%d",i); dispatch_group_leave(group); } //等待上面的任务完成 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"go on");
Dispatch Semaphore
dispatch semaphore也是用来做解决一些同步的问题,dispatch_semaphore_create会创建一个信号量,该函数需要传递一个信号值,dispatch_semaphore_signal会使信号值加1,如果信号值的大小等于1,dispatch_semaphore_wait会使信号值减1,并继续往下走,如果信号值为0,则等待。
//创建一个信号量,初始值为0 dispatch_semaphore_t sema = dispatch_semaphore_create(0); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"do some job"); sleep(1); NSLog(@"increase the semaphore"); dispatch_semaphore_signal(sema); //信号值加1 }); dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);//等待直到信号值大于等1 NSLog(@"go on");
执行结果如下:
GCDTests[1394:92383] do some job GCDTests[1394:92383] increase the semaphore GCDTests[1394:92326] go on
Dispatch Timer
dispatch timer通常配合dispatch_after使用,完成一些延时的任务:
//延迟5秒后执行任务 dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)); dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@"do job afer 5 seconds"); });
Dispatch IO
当我们要读取一份较大文件的时候,多个线程同时去读肯定比一个线程去读的速度要快,要实现这样的功能可以通过dispatch io跟dispatch data来实现,通过dispatch io去读文件时,会使用global dispatch queue将一个文件按照一个指定的分块大小同时去读取数据,类似于:
dispatch_async(queue, ^{/* 读取0-99字节 */}); dispatch_async(queue, ^{/* 读取100-199字节 */}); dispatch_async(queue, ^{/* 读取200-299字节 */}); ...
将文件分成一块一块并行的去读取,读取的数据通过Dispatch Data可以更为简单地进行结合和分割 。
- dispatch_io_create
生成Dispatch IO,指定发生错误时用来执行处理的Block,以及执行该Block的Dispatch Queue- dispatch_io_set_low_water
设定一次读取的大小(分割的大小)- dispatch_io_read
使用Global Dispatch Queue开始并列读取,当每个分割的文件块读取完毕时,会将含有文件数据的dispatch data返回到dispatch_io_read设定的block,在block中需要分析传递过来的dispatch data进行合并处理可以看下苹果的系统日志API(Libc-763.11 gen/asl.c)的源代码使用到了dispatch IO:源码地址
//dispatch_io_create出错时handler执行的队列 pipe_q = dispatch_queue_create("PipeQ", NULL); pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){ //出错时执行的handler close(fd); }); *out_fd = fdpair[1]; //设定一次读取的大小(分割大小) dispatch_io_set_low_water(pipe_channel, SIZE_MAX); dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){ if (error) return; if (err == 0) { //每次读取到数据进行数据的处理 size_t len = dispatch_data_get_size(pipedata); if (len > 0) { const char *bytes = NULL; char *encoded; uint32_t eval; dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len); encoded = asl_core_encode_buffer(bytes, len); asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded); free(encoded); eval = _asl_evaluate_send(NULL, (aslmsg)aux, -1); _asl_send_message(NULL, eval, aux, NULL); asl_msg_release(aux); dispatch_release(md); } } if (done) { //并发读取完毕 dispatch_semaphore_signal(sem); dispatch_release(pipe_channel); dispatch_release(pipe_q); } });
假如你的数据文件比较大,可以考虑采用dispatch IO的方式来提高读取的速率。
Dispatch Source
dispatch框架提供一套接口用于监听系统底层对象(如文件描述符、Mach端口、信号量等),当这些对象有事件产生时会自动把事件的处理block函数提交到dispatch队列中执行,这套接口就是Dispatch Source API,Dispatch Source其实就是对kqueue功能的封装,可以去查看dispatch_source的c源码实现(什么是kqueue?Google,什么是Mach端口? Google Again),Dispatch Source主要处理以下几种事件:
DISPATCH_SOURCE_TYPE_DATA_ADD 变量增加 DISPATCH_SOURCE_TYPE_DATA_OR 变量OR DISPATCH_SOURCE_TYPE_MACH_SEND Mach端口发送 DISPATCH_SOURCE_TYPE_MACH_RECV Mach端口接收 DISPATCH_SOURCE_TYPE_MEMORYPRESSURE 内存压力情况变化 DISPATCH_SOURCE_TYPE_PROC 与进程相关的事件 DISPATCH_SOURCE_TYPE_READ 可读取文件映像 DISPATCH_SOURCE_TYPE_SIGNAL 接收信号 DISPATCH_SOURCE_TYPE_TIMER 定时器事件 DISPATCH_SOURCE_TYPE_VNODE 文件系统变更 DISPATCH_SOURCE_TYPE_WRITE 可写入文件映像
当有事件发生时,dispatch source自动将一个block放入一个dispatch queue执行。
dispatch_source_create
创建一个dispatch source,需要指定事件源的类型,handler的执行队列,dispatch source创建完之后将处于挂起状态。此时dispatch source会接收事件,但是不会进行处理,你需要设置事件处理的handler,并执行额外的配置;同时为了防止事件堆积到dispatch queue中,dispatch source还会对事件进行合并,如果新事件在上一个事件处理handler执行之前到达,dispatch source会根据事件的类型替换或者合并新旧事件。dispatch_source_set_event_handler
给指定的dispatch source设置事件发生的处理handlerdispatch_source_set_cancel_handler
给指定的dispatch source设置一个取消处理handler,取消处理handler会在dispatch soruce释放之前做些清理工作,比如关闭文件描述符:dispatch_source_set_cancel_handler(mySource, ^{ close(fd); //关闭文件秒速符 });
- dispatch_source_cancel
异步地关闭dispatch source,这样后续的事件发生时不去调用对应的事件处理handler,但已经在执行的handler不会被取消。很多第三方库会用到dispatch source的功能,比如著名的IM框架XMPPFramework在涉及到定时器的时候都采用这种方法,比如发送心跳包的时候(setupKeepAliveTimer)。
一个简单的例子://如果dispatch source是本地变量,会被释放掉,需要这么声明 @property (nonatomic)dispatch_source_t timerSource; //事件handler的处理队列 dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL); // _timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); //定时器间隔时间 uint64_t interval = 2 * NSEC_PER_SEC; //设置定时器信息 dispatch_source_set_timer(_timerSource,DISPATCH_TIME_NOW, interval , 0); //设置事件的处理handler dispatch_source_set_event_handler(_timerSource, ^{ NSLog(@"receive time event"); //if (done) // dispatch_source_cancel(_timerSource); }); //开始处理定时器事件,dispatch_suspend暂停处理事件 dispatch_resume(_timerSource);
定时器还可以通过NSTimer实现,不过NSTimer会跟runloop关联在一起,主线层默认有一个runloop,假如你nstimer是运行在子线程,就需要自己手动开启一个runloop,而且nstimer默认是在NSDefaultRunLoopMode模式下的,所以当runloop切换到其它模式nstimer就不会运行,需要手动将nstimer添加到NSRunLoopCommonModes模式下;而dispatch source timer不跟runloop关联,所以有些场景可以使用这种方法。
本文总结了GCD的一些用法,不过有些API可能iOS8之后才可以用,如有还有什么可以补充的,欢迎提出~
部分参考