1、GCD是异步执行任务的技术之一,通常将应用程序中记叙的线程管理用的代码在系统级中是实现。 html
开发者只须要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。 编程
2、多线程编程 多线程
因为使用多线程的程序能够在某个线程和其余线程之间反复屡次进行上下文切换,所以看上去就好像一个CPU核可以并行的执行多个线程同样,并且在具备多个CPU核的状况下,就不是“看上去像”了,而是真的提供了多个CPU核并行执行任务的多个线程的技术。 并发
多线程编程容易发生各类编程问题:好比多个线程更新相同的资源会致使数据的不一致(数据竞争)、中止等待事件的线程会致使多个线程相互持续等待(死锁)、使用太多线程会消耗大量的内存资源等等。 app
尽管会有问题,可是为何还要用呢? 异步
在应用程序启动时,经过最早执行的线程,即主线程来描绘用户界面、处理用户触摸事件等,可是若是在该主线程中进行长时间的处理,就会妨碍主线程中被称为RunLoop的主循环的执行,从而致使不能跟新用户界面,应用程序的画面长时间停滞等问题。 async
3、GCD 的 API 函数
1.Dispatch Queue oop
开发者要作的只是定义想要执行的任务并追加到适当的DispatchQueue中。 spa
在Block中定义想要执行的任务,而后追加到Dispatch Queue中
Dispatch Queue是执行处理的等待队列,经过dispatch_async等API,在Block语法中记叙想要执行的处理并将其追加到Dispatch Queue中,Dispatch Queue按照追加的顺序(FIFO)执行处理。
另外,在执行处理时存在两种Dispatch Queue:
Dispatch Queue的种类
说明
Serial Dispatch Queue
等待如今执行中处理结束
Concurrent Dispatch Queue
不等待如今执行中处理结束
解释说明一下:
(1) Serial Dispatch Queue:就是要等待如今执行中处理结束后才能够进行下一个任务的执行处理,假如如今有blk1,blk2,blk3,在Serial Dispatch Queue中,那么同时执行处理数只能是一个,并且按按添加顺序FIFO进行处理,即先执行blk1,执行结束后再执行blk2,执行结束再进行blk3的执行。
(2) Concurrent Dispatch Queue:就是一个线程的执行不等待如今(当前)执行中的任务处理结束就能够开始另外一个任务的执行处理。一样假若有blk1,blk2,blk3在Concurrent Dispatch Queue中,那么首先执行blk1,无论blk1是否执行处理结束,都开始执行后面的blk2,无论blk2是否执行结束,都开始执行后面的blk2。
这样虽然不用等待处理结束,能够并行执行多个任务处理,可是并行处理数取决于当前系统的状态,有它决定Concurrent Dispatch Queue中并行执行的处理数。所谓并行执行就是使用多个线程来同时执行多个处理任务(block中的执行任务)。
SerialDispatchQueue同时只能执行一个追加处理
ConcurrentDispatchQueue并行执行多个追加处理
虽然SerialDispatchQueue ConcurrentDispatchQueue受到系统资源的限制,可是用dispatch_queue_create能够生成任意多个Dispatch Queue
当生成多个SerialDispatchQueue时,各个SerialDispatchQueue将并行执行,虽然一个SerialDispatchQueue同时只能执行一个追加处理,可是若是将处理分别追加到4个
Serial Dispatch Queue中,各个Serial Dispatch Queue执行一个,即为同时执行4个处理
可是生成Serial Dispatch Queue的个数受系统限制
为了不多线程编程的问题之一---数据竞争,就可使用Serial Dispatch Queue。
当想并行执行且不发生数据竞争等问题时就应该使用Concurrent Dispatch Queue。
[cpp]
//如下代码是两种生成Serial Dispatch Queue的方式
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", NULL);
// dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);
//如下代码是两种生成Serial Dispatch Queue的方式
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", NULL);
// dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_create,该方法中的第一个参数指定SerialDispatchQueue的名称,DispatchQueue的名称推荐使用应用程序ID之中逆序全程域名,第二个参数指定为NULL(或者DISPATCH_QUEUE_SERIAL)时即表示生成的是Serial Dispatch Queue,指定为DISPATCH_QUEUE_CONCURRENT时即表示生成的是Concurrent Dispatch Queue
[cpp]
//如下代码生成ConcurrentDispatchQueue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("cn.edu.scnu.myConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentDispatchQueue, ^{
NSLog(@"block on my ConcurrentDispatchQueue");
});
//如下代码生成ConcurrentDispatchQueue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("cn.edu.scnu.myConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConcurrentDispatchQueue, ^{
NSLog(@"block on my ConcurrentDispatchQueue");
});
二、MainDispatch Queue 和 Global Dispatch Queue
实际上不用特地生成DispatchQueue,系统也会提供几个给咱们,就是Main DispatchQueue 和 Global Dispatch Queue
Main Dispatch Queue就是主线程中执行的Dispatch Queue,由于主线程只有一个,因此Main DispatchQueue天然就是 Serial Dispatch Queue
[cpp]
//获取Main Dispatch Queue
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
//获取Main Dispatch Queue
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
GlobalDispatch Queue是全部应用程序都可以使用的Concurrent Dispatch Queue,没有必要经过dispatch_queue_create方法逐个生成Concurrent Dispatch Queue,只要获
取GlobalDispatch Queue使用便可。
Global Dispatch Queue有四个优先级:High Priority,Default Priority,Low Priority,Background Priority
[cpp]
//获取高优先级的的Global Dispatch Queue
/*
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
*/
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//第一个参数指定Global Dispatch Queue的优先级,第二个参数指定为0
//获取高优先级的的Global Dispatch Queue
/*
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
*/
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//第一个参数指定Global Dispatch Queue的优先级,第二个参数指定为0
三、dispatch_set_target_queue
dispatch_queue_create函数生成的Dispatch Queue无论是Serial Dispatch Queue仍是Concurrent Dispatch Queue,都是使用与默认优先级的Global Dispatch Queue相同执
行优先级的线程
若是想变动生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue方法。
[cpp]
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueue);
//指定要变动执行优先级的dispatch queue为dispatch_set_target_queue方法的第一个参数,指定与要使用的执行优先级相同优先级的Global Dispatch Queue为第二个参数(目标)
//第一个参数不能够指定为系统提供的Main Dispatch Queue 和 Global Dispatch Queue
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("cn.edu.scnu.mySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueue);
//指定要变动执行优先级的dispatch queue为dispatch_set_target_queue方法的第一个参数,指定与要使用的执行优先级相同优先级的Global Dispatch Queue为第二个参数(目标)
//第一个参数不能够指定为系统提供的Main Dispatch Queue 和 Global Dispatch Queue
四、dispatch_after
想在指定时间后执行处理的状况,可使用 dispatch_after 方法来实现
[cpp]
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(@"waited at least 2.0 seconds.");
});
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(@"waited at least 2.0 seconds.");
});
值得注意的是,dispatch_after方法并非在指定时间后执行处理任务,而是在指定时间后追加处理到dispatch queue中,上面的代码在2秒后用dispatch_after方法追加
block到Main Dispatch Queue中,由于Main Dispatch Queue在主线程的RunLoop中执行,因此好比每一个1/60秒执行的RunLoop中,Block最快在2秒后执行,最慢在2秒+1/60
秒后执行,并且在Main Dispatch Queue中又大量处理追加或者主线程的处理自己有延时时,这个时间会更长。
dispatch_after这个方法的第二个参数指定要追加的dispatch queue,第三个参数指定要执行处理的Block,第一个参数是指定时间用的dispatch_time_t类型的值,在使用
dispatch_after的时候,编译器会自动帮你生成这些代码,只需修改delayInSeconds就能够了。
五、Dispatch Group
在追加到dispatch queue中的多个处理所有结束后想执行结束处理任务,这种状况会常常出现。只使用一个Serial Dispatch Queue时,只要将想执行的所有处理都追加到该
Serial Dispatch Queue种并在最后追加结束处理就能够实现。可是在使用Concurrent Dispatch Queue时或者同时使用多个dispatch queue时,就会有些复杂了,在这种状况下
就应该使用Dispatch Group
[cpp]
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
由于向 Global Dispatch Queue即Concurrent Dispatch Queue追加处理任务,多个线程并行执行,因此追加处理任务的执行顺序是不定的,执行时顺序会发生变化,可是主线程中执行结果输出done确定是最后的。
下面稍微解释一下上面的那段代码,上面由3个输出任务的block组成一个dispatch group,并把这个dispatch group添加到dispatch queue中执行,当dispatch group中的
block任务执行完毕后,dispatch_group_notify方法就会被执行到,因此它的第一个参数是group,表示其被监视。在追加到dispatch group中的所有执行处理任务执行结束后,
将第三个参数中的block任务添加到第二个参数的dispatch queue中执行,注意此时dispatch group中的因此执行任务已经执行结束了。
另外,在dispatch group中也可使用 dispatch_group_wait方法仅等待所有处理执行结束。
[cpp]
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
在这个方法中的第二个参数指定等待时间,这里使用DISPATCH_TIME_FOREVER意味着永久等待,只要属于dispatch group中的处理还没有执行结束,就会一直等待,中途不能取消。
固然如同dispatch——after方法中那样,也能够指定等待时间为1秒等等。
[cpp]
//这里指定等待时间1s,即1s后查看dispatch group中的处理是否所有执行结束
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == 0) {
//属于dispatch group中的所有处理都执行结束
}
else {
//属于dispatch group的某一个处理还在执行
}
//这里指定等待时间1s,即1s后查看dispatch group中的处理是否所有执行结束
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == 0) {
//属于dispatch group中的所有处理都执行结束
}
else {
//属于dispatch group的某一个处理还在执行
}
[cpp]
//这里也能够指定DISPATCH_TIME_NOW,则不用任何等待便可判断属于dispatch group中的处理是否所有执行结束
long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
//这里也能够指定DISPATCH_TIME_NOW,则不用任何等待便可判断属于dispatch group中的处理是否所有执行结束
long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
六、dispatch_barrier_async
[cpp]
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
});
dispatch_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
});
dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
});
dispatch_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
});
dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});
若是像上面那样简单的在dispatch_async方法中添加写入数据处理的任务,那么根据Concurrent Dispatch Queue并行执行的性质,就颇有可能不是按照上面的添加处理任务的
顺序执行,那么在blk3_reading 和 blk4_reading执行读取数据的时候,blk1_writting进行写入数据的处理尚未执行到,那么后两次的读取数据操做读取到的数据就与指望中
的不符了。
解决这个问题的处理就是 使用 dispatch_barrier_async
[cpp]
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
});
dispatch_barrier_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
});
dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
//表示执行数据读取任务
NSLog(@"blk1_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk2_reading");
});
dispatch_barrier_async(queue, ^{
//表示执行数据写入处理任务
NSLog(@"blk1_writting");
});
dispatch_async(queue, ^{
NSLog(@"blk3_reading");
});
dispatch_async(queue, ^{
NSLog(@"blk4_reading");
});
使用dispatch_barrier_async方法,它会等待在它以前添加到 Concurrent Dispatch Queue的全部处理执行结束后,才执行该处理,而后等待该处理结束后,才接着处理后续添
加到Concurrent Dispatch Queue中的处理任务。固然在dispatch_barrier_async方法以前和以后添加的处理任务能够并发执行,即不保证执行顺序,可是能够确保
dispatch_barrier_async方法添加的任务必定是只能同时执行一个,按其添加任务顺序执行的,就是说,执行完blk1_reading和blk2_reading的读取数据任务后,才是进行
blk1_writting的写入数据任务,而后才是执行接着的读取数据的任务。
七、dispatch——sync
dispatch_async方法中的async意味着“非同步”,就是将指定的block非同步的添加到dispatch qeueue中,dispatch_async方法不作任何等待。
dispatch_sync方法中的sync意味着“同步”,也就是将指定的block同步追加到dispatch queue中,在追加block的过程结束以前,dispatch_sync方法会一直等待。
一旦调用dispatch_sync,那么在指定的处理执行结束以前,该方法不会返回,dispatch_sync方法能够简化代码,也能够说是简易版的dispatch_group_wait方法
dispatch_sync方法使用简单,可是容易引发死锁
[cpp]
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"引发死锁!");
});
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"引发死锁!");
});
上面的代码在主线程中执行指定的block,并等待其执行结束,可是其实在主线程中就是在执行这些代码,因此就形成了死锁。
八、dispatch_apply
dispatch_apply 方法是 dispatch_sync 方法和Dispatch Group的关联API,该方法按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待所有处理执行结束
[cpp]
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index){
NSLog(@"%zu",index);
});
NSLog(@"done");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index){
NSLog(@"%zu",index);
});
NSLog(@"done");
由于是在Global Dispatch Queue中执行处理,因此各个处理的执行时间不定,也就是说输出1 2 3 ...的顺序不定。可是输出结果done一定是在最后的位置上的。由于dispatch_apply会等待因此的处理任务执行结束。
dispatch_apply 中的第一个参数是重复次数,第二个参数是追加对象的Dispatch Queue,第三个参数为追加的处理block,注意带参数
[cpp]
//假如对一个NSArray类对象的全部元素执行处理时,没必要一个个编写for循环
NSArray *array = [[NSArray alloc] initWithObjects:@"string1",@"string2",@"string3", nil];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index){
NSLog(@"%zu : %@",index,[array objectAtIndex:index]);
});
//假如对一个NSArray类对象的全部元素执行处理时,没必要一个个编写for循环
NSArray *array = [[NSArray alloc] initWithObjects:@"string1",@"string2",@"string3", nil];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index){
NSLog(@"%zu : %@",index,[array objectAtIndex:index]);
});
因为dispatch_apply方法也与dispatch_sync方法相同,会等待处理执行结束,所以推荐在dispatch_async函数中非同步的执行dispatch_apply方法。
例如:
[cpp]
NSArray *array = [[NSArray alloc] initWithObjects:@"string1",@"string2",@"string3", nil];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//在Glocbal Dispatch Queue中非同步执行
dispatch_async(queue, ^{
//等待dispatch_apply方法中的所有处理执行结束
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index){
NSLog(@"%zu : %@",index,[array objectAtIndex:index]);
});
//dispatch_apply方法中的所有处理任务执行结束
dispatch_async(dispatch_get_main_queue(), ^{
//在主线程中执行处理
NSLog(@"done");
});
});
NSArray *array = [[NSArray alloc] initWithObjects:@"string1",@"string2",@"string3", nil];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//在Glocbal Dispatch Queue中非同步执行
dispatch_async(queue, ^{
//等待dispatch_apply方法中的所有处理执行结束
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index){
NSLog(@"%zu : %@",index,[array objectAtIndex:index]);
});
//dispatch_apply方法中的所有处理任务执行结束
dispatch_async(dispatch_get_main_queue(), ^{
//在主线程中执行处理
NSLog(@"done");
});
});
九、dispatch_suspend 和 dispatch_resume
[cpp]
//挂起指定的dispatch queue
dispatch_suspend(<#dispatch_object_t object#>)
//恢复指定的dispatch queue
dispatch_resume(<#dispatch_object_t object#>)
//这些方法对已经执行的处理没有影响,挂起后,追加到dispatch queue中但还没有处理的在此以后中止执行,而恢复后则使得这些处理可以继续执行。
//挂起指定的dispatch queue
dispatch_suspend(<#dispatch_object_t object#>)
//恢复指定的dispatch queue
dispatch_resume(<#dispatch_object_t object#>)
//这些方法对已经执行的处理没有影响,挂起后,追加到dispatch queue中但还没有处理的在此以后中止执行,而恢复后则使得这些处理可以继续执行。
十、dispatch_once
dispatch_once方法保证在应用程序执行中只执行一次指定处理的API。
[cpp]
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//code to be executed once
});
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//code to be executed once
});
转载自:http://www.2cto.com/kf/201307/231244.html 做者:Crayon_DyS