什么是GCD?下面是苹果的官方说明。git
Grand Central Dispatch (GCD) 是异步执行任务的技术之一。通常将应用程序中记述的线程管理用的代码在系统级中实现。开发者只须要定义想执行的任务并追加到适当的
Dispatch Queue
中,GCD就能生成必要的线程并计划执行任务。也就是说GCD用咱们难以置信的很是简洁的计述方法,实现了极为复杂繁琐的多线程编程。github
一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,所以经过CPU执行的CPU命令列就比如一条无分叉的大道,其执行不会出现分歧。 这里所说的 " 一个CPU执行的CPU命令列为一条无分叉的路径" 即为 "线程"。数据库
如今一个物理的CPU芯片实际上有64个(64核)CPU,若是一个CPU核虚拟为两个CPU核工做,那么一台计算机上使用多个CPU核就是理所固然的事了,尽管如此 " 一个CPU执行的CPU命令列为一条无分叉的路径" 仍然 不变。这种无分叉的路径不止有一条,存在有多条时即为 "多线程"编程
因为使用多线程的程序能够在摸个线程和其余线程之间反复屡次进行上下文切换,所以看上去好像1个CPU核可以并列的执行多个线程同样,并且在具备多个CPU核的状况下,就不是 " 看上去像" 了,而是真的提供了多个CPU核并行执行多个线程的技术。安全
这种利用多线程编程的技术就被称为"多线程编程"。bash
开发者要作的只是定义想执行的任务并追加到适当的Dispatch Queue
中。markdown
dispatch_async(queue, ^{
//想要执行的任务
});
复制代码
该代码使用Block语法 “定义想执行的任务” ,经过dispatch_async
函数 “追加” 赋值在变量queue的Dispatch Queue
中,仅这样就可以使指定的Block在另外一个线程执行。 Dispatch Queue
是什么?如其名称所示,是执行处理的等待队列。应用程序编程人员经过dispatch_async
函数等API,在Block语法中计述想执行的处理并追加到Dispatch Queue
中。Dispatch Queue
按追加顺序(先进先出FIFO,First-In-First-Out)执行处理。 多线程
执行处理时存在两种Dispatch Queue
,一种是等待如今执行中处理的Serial Dispatch Queue
,另外一种是不等待如今执行中处理的Concurrent Dispatch Queue
。app
Dispatch Queue种类 | 说明 |
---|---|
Serial Dispatch Queue (串行) | 等待如今执行中处理结束 |
Concurrent Dispatch Queue (并行) | 不等待如今执行中处理结束 |
👇下面看这个例子:异步
dispatch_async(queue, ^{NSLog(@"block0");}); dispatch_async(queue, ^{NSLog(@"block1");}); dispatch_async(queue, ^{NSLog(@"block2");}); dispatch_async(queue, ^{NSLog(@"block3");}); dispatch_async(queue, ^{NSLog(@"block4");}); dispatch_async(queue, ^{NSLog(@"block5");}); dispatch_async(queue, ^{NSLog(@"block6");}); dispatch_async(queue, ^{NSLog(@"block7");}); 复制代码
1.当变量queue
为Serial Dispatch Queue
时,由于要等待如今执行中的处理结束。首先执行block0,block0执行结束后,接着执行block1,block1执行结束后在执行block2,如此重复,同时执行的处理数只有1个。
2.当变量queue
为Concurrent Dispatch Queue
时,由于不用等待如今执行中的处理结束。因此首先执行block0,无论block0的执行是否结束,都开始执行后面的block1,无论block1的执行是否结束,都开始执行后面的block2,如此重复循环。
如何建立Dispatch Queue
,方法有两种。
第一种方法是经过 GCD 的API生成Dispatch Queue
,经过dispatch_queue_create
函数能够生成Dispatch Queue
。
/** 建立 dispatch_queue 第一个参数: 线程名称,推荐使用应用程序ID这种逆序全程域名,也能够设置为`NULL` 第二个参数: `SerialDispatchQueue`时设置为`DISPATCH_QUEUE_SERIAL` or `NULL` `ConcurrentDispatchQueue`时设置为`DISPATCH_QUEUE_CONCURRENT` */ dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("caoxueliang.MultiThreadStudy.mySerialDispatchQueue", NULL); dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("caoxueliang.MultiThreadStudy.myConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(mySerialDispatchQueue, ^{ NSLog(@"block on mySerialDispatchQueue"); }); dispatch_async(myConcurrentDispatchQueue, ^{ NSLog(@"block on myConcurrentDispatchQueue"); }); 复制代码
第二种方法是获取系统标准的Dispatch Queue
。 Main Dispatch Queue
正如其名称中含有的Main
同样,是在主线程中执行的Dispatch Queue,由于主线程只有一个,因此Main Dispatch Queue
天然就是Serial Dispatch Queue
。 追加到Main Dispatch Queue
的处理在主线程的RunLoop中执行,所以要将用户界面更新等一些必须在主线程中执行的处理追加到Main Dispatch Queue
使用。 另外一个Global Dispatch Queue
是全部应用程序都可以使用的Concurrent Dispatch Queue
,没有必要经过dispatch_queue_create
函数逐个生成Concurrent Dispatch Queue
,只要获取Global Dispatch Queue
使用便可。
//获取系统标准提供的 Dispatch Queue dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue(); dispatch_async(mainDispatchQueue, ^{ NSLog(@"主线程"); }); dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(globalDispatchQueue, ^{ NSLog(@"globalDispatchQueue"); }); 复制代码
Global Dispatch Queue
有4个执行优先级,经过XUN内核管理的用于Global Dispatch Queue
的线程,将各自使用的Global Dispatch Queue
的执行优先级做为线程的执行优先级使用。可是经过XUN内核用于Global Dispatch Queue
的线程,并不能保证明时性,所以执行优先级只是大体的判断。
Dispatch Queue
的种类:
名称 | Dispatch Queue种类 | 说明 |
---|---|---|
Main Dispatch Queue | Serial Dispatch Queue | 主线程执行 |
Global Dispatch Queue (High Priority) | Global Dispatch Queue | 执行优先级: 高 |
Global Dispatch Queue (Default Priority) | Global Dispatch Queue | 执行优先级: 默认 |
Global Dispatch Queue (Low Priority) | Global Dispatch Queue | 执行优先级: 低 |
Global Dispatch Queue (Background Priority) | Global Dispatch Queue | 执行优先级: 后台 |
Main Dispatch Queue
和Global Dispatch Queue
结合使用的例子:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*
* 可并行执行的处理
*
dispatch_async(dispatch_get_main_queue(), ^{
//只能在主线程中执行的处理,更新UI
});
});
复制代码
dispatch_queue_create
函数生成的Dispatch Queue
,不论是Serial Dispatch Queue
仍是Concurrent Dispatch Queue
,都使用与默认优先级Global Dispatch Queue
相同执行优先级的线程,而要变动生成的Dispatch Queue的执行优先级,要使用dispatch_set_target_queue
函数。
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("caoxueliang.MultiThreadStudy.mySerialDispatchQueue", NULL); dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); /* 变动生成的Dispatch Queue 的执行优先级 第一个参数: 要变动执行优先级的Dispatch Queue 第二个参数: 指定与要使用的执行优先级相同优先级的`globalDispatchQueue` */ dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueue); 复制代码
须要注意的是:第一个参数不能指定为系统提供的Main Dispatch Queue
和Global Dispatch Queue
。
在指定的时间后执行处理,好比3秒后执行处理,可以使用dispatch_after
函数来实现。在3秒后将指定的Block,追加到Main Dispatch Queue
中:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)); dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@"waited at least three seconds"); }); 复制代码
须要注意的是,dispatch_after
函数并非在指定时间后执行处理,而只是在指定时间追加处理到Dispatch Queue
,此代码与在三秒后用dispatch_async
函数,追加Block到Main Dispatch Queue
相同。
由于Main Dispatch Queue
在主线程的RunLoop中执行,因此在好比每隔1/60秒执行的RunLoop中,Block最快在3秒后执行,最慢在3秒+1/60秒后执行,而且在Main Dispatch Queue
有大量处理追加或主线程的处理自己有延迟时,这个时间会更长。 虽然在有严格时间的要求下使用时会出现问题,但在想大体延迟执行处理时,该函数是很是有效的。 dispatch_time
函数一般用于计算相对时间,而dispatch_walltime
函数用于计算绝对时间,例如在dispatch_after
函数中指定2011年11月11日11分11秒这一绝对时间的状况。
由NSDate类对象获取传递给dispatch_after
函数的dispatch_time_t
类型的值:
static inline dispatch_time_t dispatch_walltime_date(NSDate *date) { NSTimeInterval interval; double second, subsecond; struct timespec time; dispatch_time_t milestone; interval = [date timeIntervalSince1970]; subsecond = modf(interval, &second); time.tv_sec = second; time.tv_nsec = subsecond * NSEC_PER_SEC; milestone = dispatch_walltime(&time, 0); return milestone; } 复制代码
在追加到Dispatch Queue
中的多个处理所有结束后想执行结束处理,这种状况会常常出现,只是用一个Serial Dispatch Queue
时,只要将想执行的处理所有追加到该Serial Dispatch Queue
中并在最后追加结束处理,便可实现,可是使用Concurrent Dispatch Queue
时或同时使用多个Dispatch Queue
时,源代码就会变得很是复杂。
这种状况下应该使用Dispatch Group
,例以下载3张图片,只有当这3张图片都下载完成时,才会走结束处理的Block。
/* 在追加到 Dispatch Queue 中的多个处理所有结束后,执行结束处理 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSURL *imageUrl = [NSURL URLWithString:@"https://wx1.sinaimg.cn/mw690/9bbc284bgy1flt5w1kf5gj20dw0ku13h.jpg"]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; if (imageData) { NSLog(@"block1"); } }); dispatch_group_async(group, queue, ^{ NSURL *imageUrl = [NSURL URLWithString:@"https://wx3.sinaimg.cn/mw690/9bbc284bgy1fly7dmgh87j20gq0r6akh.jpg"]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; if (imageData) { NSLog(@"block2"); } }); dispatch_group_async(group, queue, ^{ NSURL *imageUrl = [NSURL URLWithString:@"https://wx3.sinaimg.cn/mw690/9bbc284bgy1fly7dmgh87j20gq0r6akh.jpg"]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; if (imageData) { NSLog(@"block3"); } }); dispatch_group_notify(group, queue, ^{ NSLog(@"执行完毕"); }); 复制代码
上面代码的执行结果为:
2017-12-06 23:28:01.661591+0800 MultiThreadStudy[1329:81841] block1
2017-12-06 23:28:01.802706+0800 MultiThreadStudy[1329:81846] block3
2017-12-06 23:28:01.886015+0800 MultiThreadStudy[1329:81843] block2
2017-12-06 23:28:01.886432+0800 MultiThreadStudy[1329:81843] 执行完毕
复制代码
由于向Global Dispatch Queue
即Concurrent Dispatch Queue
追加处理,多个线程并行执行,因此追加处理的执行顺序不定,执行时会发生变化,可是最后执行完毕
必定是最后输出的。
在访问数据库或文件时,如上所述,使用Serial Dispatch Queue
可避免数据竞争的问题,写入处理确实不可与其余的写入处理以及包含读取处理的其余某些处理并行执行,可是若是读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。 也就是说,为了高效率的进行访问,读取处理追加到Concurrent Dispatch Queue
中,写入处理在任一个读取处理没有执行的状态下,追加到Serial Dispatch Queue
中便可(在写入处理结束以前,读取处理不可执行)。 Dispatch_barrier_async
函数同dispatch_queue_create
函数生成的Concurrent Dispatch Queue
一块儿使用。
在block3_for_reading处理和block4_for_reading处理之间执行写入处理,并将写入的内容读取block4_for_reading处理以及以后的处理中。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{sleep(4); NSLog(@"block0_for_reading");}); dispatch_async(queue, ^{sleep(1); NSLog(@"block1_for_reading");}); dispatch_async(queue, ^{sleep(2); NSLog(@"block2_for_reading");}); dispatch_async(queue, ^{sleep(2); NSLog(@"block3_for_reading");}); dispatch_async(queue, ^{sleep(3);NSLog(@"写入处理");}); dispatch_async(queue, ^{sleep(1); NSLog(@"block4_for_reading");}); dispatch_async(queue, ^{sleep(2); NSLog(@"block5_for_reading");}); dispatch_async(queue, ^{sleep(4); NSLog(@"block6_for_reading");}); dispatch_async(queue, ^{NSLog(@"block7_for_reading");}); 复制代码
运行结果以下所示:
2017-12-11 22:36:10.379768+0800 MultiThreadStudy[758:22271] block7_for_reading
2017-12-11 22:36:11.381917+0800 MultiThreadStudy[758:22260] block1_for_reading
2017-12-11 22:36:11.381963+0800 MultiThreadStudy[758:22268] block4_for_reading
2017-12-11 22:36:12.382093+0800 MultiThreadStudy[758:22261] block2_for_reading
2017-12-11 22:36:12.382097+0800 MultiThreadStudy[758:22257] block3_for_reading
2017-12-11 22:36:12.382124+0800 MultiThreadStudy[758:22269] block5_for_reading
2017-12-11 22:36:13.382211+0800 MultiThreadStudy[758:22267] 写入处理
2017-12-11 22:36:14.382834+0800 MultiThreadStudy[758:22258] block0_for_reading
2017-12-11 22:36:14.382854+0800 MultiThreadStudy[758:22270] block6_for_reading
复制代码
若是像上面👆这样简单的在dispatch_async
函数中加入写入处理,那么根据Concurrent Dispatch Queue
的性质,就有可能在追加到写入处理前面的处理中读取到与期待不符的数据,还可能因非法访问致使应用程序异常结束。若是追加多个写入处理,则可能发生更多问题,好比数据竞争等。
因此,应该使用dispatch_barrier_async
函数代替dispatch_async
函数进行写入处理,以下所示:
dispatch_async(queue, ^{sleep(4); NSLog(@"block0_for_reading");}); dispatch_async(queue, ^{sleep(1); NSLog(@"block1_for_reading");}); dispatch_async(queue, ^{sleep(2); NSLog(@"block2_for_reading");}); dispatch_async(queue, ^{sleep(2); NSLog(@"block3_for_reading");}); dispatch_barrier_async(queue, ^{ NSLog(@"写入处理"); }); dispatch_async(queue, ^{sleep(1); NSLog(@"block4_for_reading");}); dispatch_async(queue, ^{sleep(2); NSLog(@"block5_for_reading");}); dispatch_async(queue, ^{sleep(4); NSLog(@"block6_for_reading");}); dispatch_async(queue, ^{NSLog(@"block7_for_reading");}); 复制代码
运行结果以下:
2017-12-11 22:52:40.062396+0800 MultiThreadStudy[834:30834] block1_for_reading
2017-12-11 22:52:41.062253+0800 MultiThreadStudy[834:30832] block2_for_reading
2017-12-11 22:52:41.062253+0800 MultiThreadStudy[834:30835] block3_for_reading
2017-12-11 22:52:43.062270+0800 MultiThreadStudy[834:30831] block0_for_reading
2017-12-11 22:52:43.062679+0800 MultiThreadStudy[834:30831] 写入处理
2017-12-11 22:52:43.063032+0800 MultiThreadStudy[834:30834] block7_for_reading
2017-12-11 22:52:44.063647+0800 MultiThreadStudy[834:30831] block4_for_reading
2017-12-11 22:52:45.065397+0800 MultiThreadStudy[834:30835] block5_for_reading
2017-12-11 22:52:47.065416+0800 MultiThreadStudy[834:30832] block6_for_reading
复制代码
所以咱们要使用dispatch_barrier_async
函数,该函数会等待追加到Concurrent Dispatch Queue
上的并行执行的处理所有结束以后,再将指定的处理追加到该Dispatch Dispatch Queue
中,而后在由dispatch_barrier_async
函数追加的处理执行完毕后,Concurrent Dispatch Queue
才恢复为通常的动做,追加到该Concurrent Dispatch Queue
的处理又开始并行执行。
dispatch_async
函数的async
意味着非同步,就是将指定的Block非同步地追加到指定的Dispatch_Queue
中,dispatch_async
函数不作任何等待,不等待处理执行结束。 既然有async
,固然也就有sync
,即dispatch_sync
函数,它意味着同步,也就是将指定的Block同步追加到指定的Dispatch Queue
中,在追加Block以前,dispatch_sync
函数会一直等待。一旦调用dispatch_sync
函数使用简单,因此也容易引发问题,即死锁。
下面👇这段代码表示在Main Dispatch Queue
即主线程中执行指定的Block,并等待其执行结束,而其实在主线程中正在执行这些源代码,因此没法执行追加到Main Dispatch Queue
的Block。
dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"hello"); }); }); 复制代码
dispatch_apply
函数是dispatch_sync
函数和Dispatch Group
的关联API。该函数按指定的次数将指定的Block追加到指定的Dispatch Queue
中,并等待所有处理执行结束。
//推荐在`dispatch_async`函数中非同步的执行`dispatch_apply`函数 NSArray *tmpArray = [NSArray arrayWithObjects:@1,@2,@3,@4, nil]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ /* *Global Dispatch Queue *等待`dispatch_apply`函数中所有处理执行结束 */ dispatch_apply([tmpArray count], queue, ^(size_t index) { //并列处理包含在`Nsarray`中的所有对象 NSLog(@"%@",[tmpArray objectAtIndex:index]); }); //`dispatch_apply`函数中处理所有执行结束 //在`main dispatch queue`中非同步执行 dispatch_async(dispatch_get_main_queue(), ^{ //更新用户界面 NSLog(@"done"); }); }); 复制代码
输出结果为:
2017-12-13 21:06:55.070579+0800 MultiThreadStudy[935:29821] 2 2017-12-13 21:06:55.070579+0800 MultiThreadStudy[935:29813] 1 2017-12-13 21:06:55.070580+0800 MultiThreadStudy[935:29820] 3 2017-12-13 21:06:55.070604+0800 MultiThreadStudy[935:29817] 4 2017-12-13 21:06:55.075021+0800 MultiThreadStudy[935:29629] done 复制代码
由于在Global Dispatch Queue
中执行处理,因此各个处理的执行时间不定,但输出结果中最后的done一定在最后的位置,这是由于diapatch_apply
函数会等待所有处理执行结束。 第一个参数为重复次数,第二个参数为追加对象的Dispatch Queue
,第三个参数的Block为带参数的Block。 另外,因为dispatch_apply
函数也与dispatch_sync
函数相同,会等待处理执行结束,所以推荐在dispatch_async
函数中非同步地执行dispatch_apply
函数。
当追加大量处理到Dispatch Queue
时,在追加处理的过程当中,有时但愿不执行已追加的处理,在这种状况下,只要挂起Dispatch Queue
便可,当能够执行时在恢复。
//挂起指定的queue
dispatch_suspend(queue);
//恢复指定的queue
dispatch_resume(queue);
复制代码
这些函数对已经执行的处理没有影响,挂起后,追加到Disaptch Queue
中但还没有执行的处理在此以后中止执行,而恢复则使得这些处理可以继续执行。
信号量就是一种可用来控制访问资源的数量标识,设定一个信号量,在线程访问以前,加上信号量的处理,则告知系统按照咱们指定的信号量数量来执行多个线程。
dispatch_semaphore_create(n)
:生成信号,n表示信号量为n。dispatch_semaphore_wait
:信号等待,它像一个安保,好比小区规定最多只能进入3辆车,而进入一辆车后名额就会减小一个,当剩下的名额为0的时候,再有汽车说要进去时,就只能在外面等待了,直到有名额闲置出来了,才能开进小区。dispatch_semaphore_signal
:信号释放,当有一辆车从小区出来时,就腾出来了一个名额。/* * 生成 Dispatch Semaphone * Dispatch Semaphone 的计数初始值设置为2 */ dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //任务1 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 1"); sleep(1); NSLog(@"complete task 1"); dispatch_semaphore_signal(semaphore); }); //任务2 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 2"); sleep(1); NSLog(@"complete task 2"); dispatch_semaphore_signal(semaphore); }); //任务3 dispatch_async(quene, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 3"); sleep(1); NSLog(@"complete task 3"); dispatch_semaphore_signal(semaphore); }); 复制代码
打印结果以下:
2017-12-13 23:03:00.196126+0800 MultiThreadStudy[1284:98022] run task 2
2017-12-13 23:03:00.196126+0800 MultiThreadStudy[1284:98014] run task 1
2017-12-13 23:03:01.201554+0800 MultiThreadStudy[1284:98022] complete task 2
2017-12-13 23:03:01.201562+0800 MultiThreadStudy[1284:98014] complete task 1
2017-12-13 23:03:01.201911+0800 MultiThreadStudy[1284:98015] run task 3
2017-12-13 23:03:02.205812+0800 MultiThreadStudy[1284:98015] complete task 3
复制代码
当将信号量个数设置为1时,打印结果以下:
2017-12-13 23:04:02.907501+0800 MultiThreadStudy[1316:99128] run task 1
2017-12-13 23:04:03.912940+0800 MultiThreadStudy[1316:99128] complete task 1
2017-12-13 23:04:03.913360+0800 MultiThreadStudy[1316:99131] run task 2
2017-12-13 23:04:04.913853+0800 MultiThreadStudy[1316:99131] complete task 2
2017-12-13 23:04:04.914204+0800 MultiThreadStudy[1316:99129] run task 3
2017-12-13 23:04:05.919195+0800 MultiThreadStudy[1316:99129] complete task 3
复制代码
当将信号量个数设置为3时,则打印结果以下:
2017-12-13 23:05:22.677144+0800 MultiThreadStudy[1354:100642] run task 1
2017-12-13 23:05:22.677145+0800 MultiThreadStudy[1354:100638] run task 2
2017-12-13 23:05:22.677175+0800 MultiThreadStudy[1354:100646] run task 3
2017-12-13 23:05:23.681331+0800 MultiThreadStudy[1354:100642] complete task 1
2017-12-13 23:05:23.681333+0800 MultiThreadStudy[1354:100646] complete task 3
2017-12-13 23:05:23.681333+0800 MultiThreadStudy[1354:100638] complete task 2
复制代码
经过上面的例子,对dispatch_semaphore
已经有了基本的了解。
dispatch_once
函数是保证在应用程序执行中只执行一次指定处理的API。请看下面👇这个例子:
+ (NSBundle *)bundle { static NSBundle *bundle; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *path = [[NSBundle mainBundle] pathForResource:@"ResourceWeibo" ofType:@"bundle"]; bundle = [NSBundle bundleWithPath:path]; }); return bundle; } 复制代码
经过dispatch_once
函数建立的,该代码即便在多线程环境下执行,也能够保证百分百安全。该函数常常在生成单例对象时使用。
dispatch_source
函数能够实现定时器的功能:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); //每秒执行 dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //指定定时器指定时间内执行的处理 dispatch_source_set_event_handler(_timer, ^{ NSLog(@"text"); if(time <= 0){ //倒计时结束,关闭 dispatch_source_cancel(_timer); dispatch_async(dispatch_get_main_queue(), ^{ }); } }); //启动 Dispatch Source dispatch_resume(_timer); 复制代码
参考书籍Objective-C高级编程 文中DemoGitHub下载