- 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
- 纯C语言,提供了非常多强大的函数
- GCD的优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
同步和异步:决定的是任务的执行方式
同步: 如果我没有执行完,那么后面的将永远无法执行
异步:我无所谓,我没执行完后面的也可以执行
同步函数和异步函数:disaptach_sync| disaptach_async
同步函数:
特点:只能在当前线程中执行任务,不具备开启新线程的能力
执行方式:同步
异步函数:
特点:可以在新的线程中执行任务,具备开启新线程的能力
执行方式:异步
GCD中的队列
1)并发队列:
(1)直接创建 dispatch_queue_create DISPATCH_QUEUE_CONCURRENT
(2)全局并发队列 dispatch_get_global_queue
2)串行队列:
(1)直接创建 dispatch_queue_create DISPATCH_QUEUE_SERIAL
(2)主队列 dispatch_get_main_queue
a.凡是放在主队列中的任务都必须要在主线程中执行
b.该队列是默认存在的
c.主队列在调度任务之前会先检查主线程当前状态,如果主线程忙则暂停调度
/*
获得全局并发队列
系统内部默认提供4个全局并发队列
第一个参数:队列的优先级
第二个参数:留给未来使用 传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
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2.使用函数封装任务并且把任务添加到队列中
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
0----<NSThread: 0x600002fa4f40>{number = 7, name = (null)}
1----<NSThread: 0x600002fbccc0>{number = 6, name = (null)}
5----<NSThread: 0x600002ffa2c0>{number = 3, name = (null)}
4----<NSThread: 0x600002fbccc0>{number = 6, name = (null)}
6----<NSThread: 0x600002ffa2c0>{number = 3, name = (null)}
8----<NSThread: 0x600002fbccc0>{number = 6, name = (null)}
9----<NSThread: 0x600002ffa2c0>{number = 3, name = (null)}
7----<NSThread: 0x600002fbf2c0>{number = 5, name = (null)}
3----<NSThread: 0x600002fa4f40>{number = 7, name = (null)}
2----<NSThread: 0x600002fe7e40>{number = 4, name = (null)}
不会开启新的线程,所有的任务都在主线程中串行执行
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
0----<NSThread: 0x6000016b4980>{number = 1, name = main}
1----<NSThread: 0x6000016b4980>{number = 1, name = main}
2----<NSThread: 0x6000016b4980>{number = 1, name = main}
3----<NSThread: 0x6000016b4980>{number = 1, name = main}
4----<NSThread: 0x6000016b4980>{number = 1, name = main}
5----<NSThread: 0x6000016b4980>{number = 1, name = main}
6----<NSThread: 0x6000016b4980>{number = 1, name = main}
7----<NSThread: 0x6000016b4980>{number = 1, name = main}
8----<NSThread: 0x6000016b4980>{number = 1, name = main}
9----<NSThread: 0x6000016b4980>{number = 1, name = main}
开起了一条子线程,队列里面的任务是串行执行的
dispatch_queue_t queue = dispatch_queue_create("RJCustomTool", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
0----<NSThread: 0x600002860a80>{number = 7, name = (null)}
1----<NSThread: 0x600002860a80>{number = 7, name = (null)}
2----<NSThread: 0x600002860a80>{number = 7, name = (null)}
3----<NSThread: 0x600002860a80>{number = 7, name = (null)}
4----<NSThread: 0x600002860a80>{number = 7, name = (null)}
5----<NSThread: 0x600002860a80>{number = 7, name = (null)}
6----<NSThread: 0x600002860a80>{number = 7, name = (null)}
7----<NSThread: 0x600002860a80>{number = 7, name = (null)}
8----<NSThread: 0x600002860a80>{number = 7, name = (null)}
9----<NSThread: 0x600002860a80>{number = 7, name = (null)}
会开多条子线程,队列中的任务是并发执行的
dispatch_queue_t queue = dispatch_queue_create("RJCustomTool", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
1----<NSThread: 0x6000038785c0>{number = 6, name = (null)}
3----<NSThread: 0x6000038641c0>{number = 5, name = (null)}
5----<NSThread: 0x6000038785c0>{number = 6, name = (null)}
2----<NSThread: 0x60000383fb00>{number = 7, name = (null)}
7----<NSThread: 0x6000038641c0>{number = 5, name = (null)}
4----<NSThread: 0x60000383ef80>{number = 8, name = (null)}
9----<NSThread: 0x60000383fb00>{number = 7, name = (null)}
6----<NSThread: 0x600003878e00>{number = 4, name = (null)}
0----<NSThread: 0x600003865f40>{number = 3, name = (null)}
8----<NSThread: 0x6000038785c0>{number = 6, name = (null)}
不会开启新的线程,所有的任务都在主线程中串行执行(死锁)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];
}
-(void)task{
NSLog(@"%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
}
<NSThread: 0x600002b9ce00>{number = 6, name = (null)}
0----<NSThread: 0x600001aec980>{number = 1, name = main}
1----<NSThread: 0x600001aec980>{number = 1, name = main}
2----<NSThread: 0x600001aec980>{number = 1, name = main}
3----<NSThread: 0x600001aec980>{number = 1, name = main}
4----<NSThread: 0x600001aec980>{number = 1, name = main}
5----<NSThread: 0x600001aec980>{number = 1, name = main}
6----<NSThread: 0x600001aec980>{number = 1, name = main}
7----<NSThread: 0x600001aec980>{number = 1, name = main}
8----<NSThread: 0x600001aec980>{number = 1, name = main}
9----<NSThread: 0x600001aec980>{number = 1, name = main}
不会开启新的线程,所有的任务在当前线程中串行执行
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];
}
-(void)task{
NSLog(@"%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("RJCustomTool", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
}
<NSThread: 0x600000b508c0>{number = 8, name = (null)}
0----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
1----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
2----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
3----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
4----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
5----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
6----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
7----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
8----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
9----<NSThread: 0x600000b508c0>{number = 8, name = (null)}
不会开启新的线程,所有的任务在当前线程中串行执行
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];
}
-(void)task{
NSLog(@"%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("RJCustomTool", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"%d----%@",i,[NSThread currentThread]);
});
}
}
<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
0----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
1----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
2----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
3----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
4----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
5----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
6----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
7----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
8----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
9----<NSThread: 0x600000f3cdc0>{number = 7, name = (null)}
子线程->主线程(嵌套&使用主队列)
dispatch_async(dispatch_get_global_queue(0,0), ^{//开启全局并发队列下载图片
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1607584191107&di=dabaca87bbdb1c938cebc378ccd85480&imgtype=0&src=http%3A%2F%2Fattachments.gfan.com%2Fforum%2Fattachments2%2F201306%2F06%2F144906n0nffsz8h43i1t23.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{//回到主线程设置UI
self.imageView.image = image;
});
});
NSObject--NSTimer--GCD
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(task) withObject:nil afterDelay:2.0];
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(task) userInfo:nil repeats:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"GCD----%@",[NSThread currentThread]);
});
}
-(void)task{
NSLog(@"%@",[NSThread currentThread]);
}
<NSThread: 0x600003b74b00>{number = 1, name = main}
<NSThread: 0x600003b74b00>{number = 1, name = main}
GCD----<NSThread: 0x600003b74b00>{number = 1, name = main}
dispatch_source_cancel(timer)
之后 dispatch_resume(timer)
会报错:Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//1.创建一个GCD的定时器
/*
第一个参数:DISPATCH_SOURCE_TYPE_TIMER 定时器
第二个参数:描述信息
第三个参数:总是传0
第四个参数:队列 (决定GCD要执行的任务在哪个线程中调用的 并发队列--子线程中调用:dispatch_get_global_queue(0,0) 传主队列 —主线程中调用:dispatch_get_main_queue())*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0,0));
//2.设置定时器
/*
第一个参数:定时器对象
第二个参数:从什么时候开始计时 DISPATCH_TIME_NOW 现在
第三个参数:间隔时间 2.0 ns
第四个参数:精准度 允许的误差 绝对精准~0
——>dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);<--*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.设置定时器的任务
__block NSInteger number = 10;
dispatch_source_set_event_handler(timer, ^{
if (number < 5) {
NSLog(@"dispatch_source_cancel===%@",[NSThread currentThread]);
//dispatch_source_cancel(timer); //Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
dispatch_suspend(timer);//暂停
}else{
NSLog(@"dispatch_source_set_event_handler--%ld===%@",number,[NSThread currentThread]);
number--;
}
});
dispatch_resume(timer);
// 8秒后继续定时器
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"dispatch_after----%@",[NSThread currentThread]);
number = 8;
dispatch_resume(timer);
});
}
dispatch_source_set_event_handler--10===<NSThread: 0x6000009729c0>{number = 3, name = (null)}
dispatch_source_set_event_handler--9===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_set_event_handler--8===<NSThread: 0x6000009729c0>{number = 3, name = (null)}
dispatch_source_set_event_handler--7===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_set_event_handler--6===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_set_event_handler--5===<NSThread: 0x6000009729c0>{number = 3, name = (null)}
dispatch_source_cancel===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_after----<NSThread: 0x60000093c1c0>{number = 1, name = main}
dispatch_source_set_event_handler--8===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_set_event_handler--7===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_set_event_handler--6===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_set_event_handler--5===<NSThread: 0x600000970680>{number = 5, name = (null)}
dispatch_source_cancel===<NSThread: 0x6000009729c0>{number = 3, name = (null)}
能够保证在整个程序运行过程中,block中的代码段只会被执行一次 --- 线程安全 --- 应用:单例模式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//code to be executed once
});
能够控制并发队列里面任务的执行顺序
-
dispatch_barrier_async
:用于提交异步执行栅栏函数块任务,并立即返回.所以该函数不会阻塞线程,只会阻塞任务执行。栅栏函数提交之后并不会立即执行,而是会直接返回。等待在栅栏函数之前提交的任务都执行完成之后,栅栏函数任务会自动执行,在栅栏函数之后提交的任务必须在栅栏函数执行完成之后才会执行. -
dispatch_barrier_sync
:用于提交同步执行栅栏函数块任务,并不会立即返回,需要等待栅栏函数提交的任务执行完毕之后才会返回。与dispatch_barrier_async不同,由于该函数需要同步执行,在该栅栏函数任务执行完成之前,函数不会返回,所以此时后边的任务都会被阻塞.
注意:不能使用全局并发队列 (relevant only on DISPATCH_QUEUE_CONCURRENT queues).
dispatch_barrier_async
dispatch_queue_t queue = dispatch_queue_create("RJCustomTool", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"task %d --- %@",i, [NSThread currentThread]);
});
}
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async --- %@", [NSThread currentThread]);
});
NSLog(@"task end --- %@", [NSThread currentThread]);
task end --- <NSThread: 0x600003b703c0>{number = 1, name = main}
task 2 --- <NSThread: 0x600003b20a40>{number = 6, name = (null)}
task 4 --- <NSThread: 0x600003b75e80>{number = 5, name = (null)}
task 5 --- <NSThread: 0x600003b14040>{number = 8, name = (null)}
task 6 --- <NSThread: 0x600003b20a40>{number = 6, name = (null)}
task 8 --- <NSThread: 0x600003b75e80>{number = 5, name = (null)}
task 1 --- <NSThread: 0x600003b76f40>{number = 3, name = (null)}
task 0 --- <NSThread: 0x600003b38a00>{number = 4, name = (null)}
task 3 --- <NSThread: 0x600003b2a100>{number = 7, name = (null)}
task 7 --- <NSThread: 0x600003b76e40>{number = 9, name = (null)}
task 9 --- <NSThread: 0x600003b20a40>{number = 6, name = (null)}
dispatch_barrier_async --- <NSThread: 0x600003b20a40>{number = 6, name = (null)}
dispatch_barrier_sync
dispatch_queue_t queue = dispatch_queue_create("RJCustomTool", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"task %d --- %@",i, [NSThread currentThread]);
});
}
dispatch_barrier_sync(queue, ^{
NSLog(@"dispatch_barrier_async --- %@", [NSThread currentThread]);
});
NSLog(@"task end --- %@", [NSThread currentThread]);
task 1 --- <NSThread: 0x6000036e71c0>{number = 6, name = (null)}
task 2 --- <NSThread: 0x6000036e0800>{number = 5, name = (null)}
task 4 --- <NSThread: 0x6000036a1e80>{number = 7, name = (null)}
task 6 --- <NSThread: 0x6000036e71c0>{number = 6, name = (null)}
task 7 --- <NSThread: 0x6000036e0800>{number = 5, name = (null)}
task 8 --- <NSThread: 0x6000036a1e80>{number = 7, name = (null)}
task 5 --- <NSThread: 0x6000036ef480>{number = 8, name = (null)}
task 0 --- <NSThread: 0x6000036e01c0>{number = 4, name = (null)}
task 3 --- <NSThread: 0x6000036a3300>{number = 3, name = (null)}
task 9 --- <NSThread: 0x6000036e71c0>{number = 6, name = (null)}
dispatch_barrier_async --- <NSThread: 0x6000036a43c0>{number = 1, name = main}
task end --- <NSThread: 0x6000036a43c0>{number = 1, name = main}
快速迭代不能使用主队列
1)for循环和apply的区别(for是在当前线程中串行执行)
01)主线程和子线程一起执行迭代任务
02)所有的任务是在多个线程(包含主线程)中并发执行
2)如何拼接文件全路径(stringByAppendingPathComponent
3)NSFileManager的基本使用(获得路径下面所有子路径&剪切文件)
4)注意点:快速迭代不能使用主队列
使用dispatch_get_main_queue报错:Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t i) {
NSLog(@"%zd---%@",i,[NSThread currentThread]);
});
NSLog(@"---end")
0---<NSThread: 0x600000a14280>{number = 1, name = main}
3---<NSThread: 0x600000a14280>{number = 1, name = main}
1---<NSThread: 0x600000a5ce00>{number = 4, name = (null)}
4---<NSThread: 0x600000a1c340>{number = 6, name = (null)}
7---<NSThread: 0x600000a5ce00>{number = 4, name = (null)}
5---<NSThread: 0x600000a14280>{number = 1, name = main}
8---<NSThread: 0x600000a1c340>{number = 6, name = (null)}
2---<NSThread: 0x600000a52c80>{number = 5, name = (null)}
9---<NSThread: 0x600000a5ce00>{number = 4, name = (null)}
6---<NSThread: 0x600000a1e300>{number = 7, name = (null)}
---end
剪切文件
/Users/jintian/Desktop/from
//1.获得文件原始路径(上层文件夹得路径)
NSString *fromPath = @"/Users/jintian/Desktop/from";
//2.获得文件的目标路径
NSString *toPath = @"/Users/jintian/Desktop/to";
//3.得到文件路径下面的所有文件(3)
NSArray *subpaths = [[NSFileManager defaultManager] subpathsAtPath:fromPath];
NSInteger count = subpaths.count;
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index){
NSLog(@"%zd---%@",index,[NSThread currentThread]);
//4.拼接全路径
NSString *fromFullpath = [fromPath stringByAppendingPathComponent:subpaths[index]];
NSString *toFullpath = [toPath stringByAppendingPathComponent:subpaths[index]];
//5.执行文件剪切操作
//第一个参数:文件在哪里的全路径
//第二个参数:文件要被剪切到哪里的全路径
[[NSFileManager defaultManager] moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
0---<NSThread: 0x60000346ca40>{number = 1, name = main}
2---<NSThread: 0x600003434d40>{number = 7, name = (null)}
3---<NSThread: 0x60000343c200>{number = 8, name = (null)}
1---<NSThread: 0x6000034211c0>{number = 5, name = (null)}
4---<NSThread: 0x600003424840>{number = 3, name = (null)}
5---<NSThread: 0x600003469640>{number = 6, name = (null)}
6---<NSThread: 0x6000034211c0>{number = 5, name = (null)}
7---<NSThread: 0x600003434d40>{number = 7, name = (null)}
8---<NSThread: 0x60000346ca40>{number = 1, name = main}
9---<NSThread: 0x600003424840>{number = 3, name = (null)}
10---<NSThread: 0x600003469640>{number = 6, name = (null)}
待研究
1)队列组的第一种用法(推荐)
(1)创建队列组 dispatch_group_create()
dispatch_group_t group = dispatch_group_create();
(2)队列组监听任务的执行 dispatch_group_async
(3)拦截通知 dispatch_group_notify(注意:该方法是异步的)
dispatch_group_notify(group, queue, ^{ });
2)队列组的第二种用法(能看懂别人的代码)
(1)dispatch_group_enter(组名) & dispatch_group_leave(组名) 必须成对使用
(2)dispatch_group_wait的用法(注意:该方法本身是同步的)
不管队列中的任务有没有执行完毕都继续往下执行,如果在该时间内所有事任务都执行完毕了那么会返回一个0,否则是非0值
long n = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
3)如何下载图片并合成
队列组|栅栏函数
4)GCD补充知识
(1)使用函数封装任务
//异步函数,该方法使用函数来封装任务
dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work)
-(void)test{dispatch_async_f(dispatch_get_global_queue(0, 0), NULL(传给要调用函数的参数 可以不传), task);}
void task (void *param){ NSLog(@"1----%@",[NSThread currentThread]); }
(2)全局并发队列和create创建并发队列的区别
(3)GCD对象的释放(iOS6.0之后不再需要手动释放)
dispatch_queue_t queue = dispatch_queue_create("key", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
dispatch_group_enter(group);
if(success){
dispatch_group_leave(group);
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
});
dispatch_queue_t queue = dispatch_queue_create("key", DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
if(success){
dispatch_semaphore_signal(semaphore);
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, queue, ^{
});
``
``
``
``
``
网友评论