- 浏览: 22411 次
- 性别:
- 来自: 北京
最新评论
GCD使用注意
- 博客分类:
- ios
转载:http://www.cocoachina.com/ios/20150505/11751.html
前言
GCD(Grand Central Dispatch)可以说是Mac、iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧。
dispatch_once_t必须是全局或static变量
这一条算是“老生常谈”了,但我认为还是有必要强调一次,毕竟非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug,正确的如下:
1
2
3
4
5
//静态变量,保证只有一份实例,才能确保只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//单例代码
});
其实就是保证dispatch_once_t只有一份实例。
dispatch_queue_create的第二个参数
dispatch_queue_create,创建队列用的,它的参数只有两个,原型如下:
1
dispatch_queue_t dispatch_queue_create ( const char *label, dispatch_queue_attr_t attr );
在网上的大部分教程里(甚至Apple自己的文档里),都是这么创建串行队列的:
1
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
看,第二个参数传的是“NULL”。 但是dispatch_queue_attr_t类型是有已经定义好的常量的,所以我认为,为了更加的清晰、严谨,最好如下创建队列:
1
2
3
4
//串行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_CONCURRENT);
常量就是为了使代码更加“易懂”,更加清晰,既然有,为啥不用呢~
dispatch_after是延迟提交,不是延迟运行
先看看官方文档的说明:
1
Enqueue a block for execution at the specified time.
Enqueue,就是入队,指的就是将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行!。
看看如下代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_CONCURRENT);
//立即打印一条信息
NSLog(@"Begin add block...");
//提交一个block
dispatch_async(queue, ^{
//Sleep 10秒
[NSThread sleepForTimeInterval:10];
NSLog(@"First block done...");
});
//5 秒以后提交block
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
NSLog(@"After...");
});
结果如下:
1
2
3
2015-03-31 20:57:27.122 GCDTest[45633:1812016] Begin add block...
2015-03-31 20:57:37.127 GCDTest[45633:1812041] First block done...
2015-03-31 20:57:37.127 GCDTest[45633:1812041] After...
从结果也验证了,dispatch_after只是延时提交block,并不是延时后立即执行。所以想用dispatch_after精确控制运行状态的朋友可要注意了~
正确创建dispatch_time_t
用dispatch_after的时候就会用到dispatch_time_t变量,但是如何创建合适的时间呢?答案就是用dispatch_time函数,其原型如下:
1
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一个参数一般是DISPATCH_TIME_NOW,表示从现在开始。
那么第二个参数就是真正的延时的具体时间。
这里要特别注意的是,delta参数是“纳秒!”,就是说,延时1秒的话,delta应该是“1000000000”=。=,太长了,所以理所当然系统提供了常量,如下:
1
2
3
#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
关键词解释:
NSEC:纳秒。
USEC:微妙。
SEC:秒
PER:每
所以:
NSEC_PER_SEC,每秒有多少纳秒。
USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)
NSEC_PER_USEC,每毫秒有多少纳秒。
所以,延时1秒可以写成如下几种:
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
最后一个“USEC_PER_SEC * NSEC_PER_USEC”,翻译过来就是“每秒的毫秒数乘以每毫秒的纳秒数”,也就是“每秒的纳秒数”,所以,延时500毫秒之类的,也就不难了吧~
dispatch_suspend != 立即停止队列的运行
dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block,看如下例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一个block,延时5秒打印。
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds...");
});
//提交第二个block,也是延时5秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds again...");
});
//延时一秒
NSLog(@"sleep 1 second...");
[NSThread sleepForTimeInterval:1];
//挂起队列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延时10秒
NSLog(@"sleep 10 second...");
[NSThread sleepForTimeInterval:10];
//恢复队列
NSLog(@"resume...");
dispatch_resume(queue);
运行结果如下:
1
2
3
4
5
6
2015-04-01 00:32:09.903 GCDTest[47201:1883834] sleep 1 second...
2015-04-01 00:32:10.910 GCDTest[47201:1883834] suspend...
2015-04-01 00:32:10.910 GCDTest[47201:1883834] sleep 10 second...
2015-04-01 00:32:14.908 GCDTest[47201:1883856] After 5 seconds...
2015-04-01 00:32:20.911 GCDTest[47201:1883834] resume...
2015-04-01 00:32:25.912 GCDTest[47201:1883856] After 5 seconds again...
可知,在dispatch_suspend挂起队列后,第一个block还是在运行,并且正常输出。
结合文档,我们可以得知,dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。
所以下次想暂停正在队列上运行的block时,还是不要用dispatch_suspend了吧~
“同步”的dispatch_apply
dispatch_apply的作用是在一个队列(串行或并行)上“运行”多次block,其实就是简化了用循环去向队列依次添加block任务。但是我个人觉得这个函数就是个“坑”,先看看如下代码运行结果:
1
2
3
4
5
6
7
8
//创建异步串行队列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//运行block3次
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
});
//打印信息
NSLog(@"After apply");
运行的结果是:
1
2
3
4
2015-04-01 00:55:40.854 GCDTest[47402:1893289] apply loop: 0
2015-04-01 00:55:40.856 GCDTest[47402:1893289] apply loop: 1
2015-04-01 00:55:40.856 GCDTest[47402:1893289] apply loop: 2
2015-04-01 00:55:40.856 GCDTest[47402:1893289] After apply
看,明明是提交到异步的队列去运行,但是“After apply”居然在apply后打印,也就是说,dispatch_apply将外面的线程(main线程)“阻塞”了!
查看官方文档,dispatch_apply确实会“等待”其所有的循环运行完毕才往下执行=。=,看来要小心使用了。
避免死锁!
dispatch_sync导致的死锁
涉及到多线程的时候,不可避免的就会有“死锁”这个问题,在使用GCD时,往往一不小心,就可能造成死锁,看看下面的“死锁”例子:
1
2
3
4
//在main线程使用“同步”方法提交Block,必定会死锁。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"I am block...");
});
你可能会说,这么低级的错误,我怎么会犯,那么,看看下面的:
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)updateUI1 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Update ui 1");
//死锁!
[self updateUI2];
});
}
- (void)updateUI2 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Update ui 2");
});
}
在你不注意的时候,嵌套调用可能就会造成死锁!所以为了“世界和平”=。=,我们还是少用dispatch_sync吧。
dispatch_apply导致的死锁!
啥,dispatch_apply导致的死锁?。。。是的,前一节讲到,dispatch_apply会等循环执行完成,这不就差不多是阻塞了吗。看如下例子:
1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
//再来一个dispatch_apply!死锁!
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"apply loop inside %zu", j);
});
});
这端代码只会输出“apply loop: 1”。。。就没有然后了=。=
所以,一定要避免dispatch_apply的嵌套调用。
灵活使用dispatch_group
很多时候我们需要等待一系列任务(block)执行完成,然后再做一些收尾的工作。如果是有序的任务,可以分步骤完成的,直接使用串行队列就行。但是如果是一系列并行执行的任务呢?这个时候,就需要dispatch_group帮忙了~总的来说,dispatch_group的使用分如下几步:
创建dispatch_group_t
添加任务(block)
添加结束任务(如清理操作、通知UI等)
下面着重讲讲在后面两步。
添加任务
添加任务可以分为以下两种情况:
自己创建队列:使用dispatch_group_async。
无法直接使用队列变量(如使用AFNetworking添加异步任务):使用dispatch_group_enter,dispatch_group_leave。
自己创建队列时,当然就用dispatch_group_async函数,简单有效,简单例子如下:
1
2
3
4
//省去创建group、queue代码。。。
dispatch_group_async(group, queue, ^{
//Do you work...
});
当你无法直接使用队列变量时,就无法使用dispatch_group_async了,下面以使用AFNetworking时的情况:
1
2
3
4
5
6
7
8
9
10
11
12
13
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//Enter group
dispatch_group_enter(group);
[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Deal with result...
//Leave group
dispatch_group_leave(group);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Deal with error...
//Leave group
dispatch_group_leave(group);
}];
//More request...
使用dispatch_group_enter,dispatch_group_leave就可以方便的将一系列网络请求“打包”起来~
添加结束任务
添加结束任务也可以分为两种情况,如下:
在当前线程阻塞的同步等待:dispatch_group_wait。
添加一个异步执行的任务作为结束任务:dispatch_group_notify
这两个比较简单,就不再贴代码了=。=
使用dispatch_barrier_async,dispatch_barrier_sync的注意事项
dispatch_barrier_async的作用就是向某个队列插入一个block,当目前正在执行的block运行完成后,阻塞这个block后面添加的block,只运行这个block直到完成,然后再继续后续的任务,有点“唯我独尊”的感觉=。=
值得注意的是:
dispatchbarrier\(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。
既然在串行队列上跟dispatch_(a)sync效果一样,那就要小心别死锁!
dispatch_set_context与dispatch_set_finalizer_f的配合使用
dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:
用C语言的malloc创建context数据。
用C++的new创建类对象。
用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。
以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用free、delete还是CF的CFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。
所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置“析构函数”,在这个函数里面释放内存,大致如下:
1
2
3
4
5
6
7
8
9
void cleanStaff(void *context) {
//释放context的内存!
//CFRelease(context);
//free(context);
//delete context;
}
...
//在队列创建后,设置其“析构函数”
dispatch_set_finalizer_f(queue, cleanStaff);
详细用法,请看我之前写的Blog为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
总结
其实本文更像是总结了GCD中的“坑”=。=
至于经验,总结一条,就是使用任何技术,都要研究透彻,否则后患无穷啊~
前言
GCD(Grand Central Dispatch)可以说是Mac、iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧。
dispatch_once_t必须是全局或static变量
这一条算是“老生常谈”了,但我认为还是有必要强调一次,毕竟非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug,正确的如下:
1
2
3
4
5
//静态变量,保证只有一份实例,才能确保只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//单例代码
});
其实就是保证dispatch_once_t只有一份实例。
dispatch_queue_create的第二个参数
dispatch_queue_create,创建队列用的,它的参数只有两个,原型如下:
1
dispatch_queue_t dispatch_queue_create ( const char *label, dispatch_queue_attr_t attr );
在网上的大部分教程里(甚至Apple自己的文档里),都是这么创建串行队列的:
1
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
看,第二个参数传的是“NULL”。 但是dispatch_queue_attr_t类型是有已经定义好的常量的,所以我认为,为了更加的清晰、严谨,最好如下创建队列:
1
2
3
4
//串行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_CONCURRENT);
常量就是为了使代码更加“易懂”,更加清晰,既然有,为啥不用呢~
dispatch_after是延迟提交,不是延迟运行
先看看官方文档的说明:
1
Enqueue a block for execution at the specified time.
Enqueue,就是入队,指的就是将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行!。
看看如下代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_CONCURRENT);
//立即打印一条信息
NSLog(@"Begin add block...");
//提交一个block
dispatch_async(queue, ^{
//Sleep 10秒
[NSThread sleepForTimeInterval:10];
NSLog(@"First block done...");
});
//5 秒以后提交block
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
NSLog(@"After...");
});
结果如下:
1
2
3
2015-03-31 20:57:27.122 GCDTest[45633:1812016] Begin add block...
2015-03-31 20:57:37.127 GCDTest[45633:1812041] First block done...
2015-03-31 20:57:37.127 GCDTest[45633:1812041] After...
从结果也验证了,dispatch_after只是延时提交block,并不是延时后立即执行。所以想用dispatch_after精确控制运行状态的朋友可要注意了~
正确创建dispatch_time_t
用dispatch_after的时候就会用到dispatch_time_t变量,但是如何创建合适的时间呢?答案就是用dispatch_time函数,其原型如下:
1
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一个参数一般是DISPATCH_TIME_NOW,表示从现在开始。
那么第二个参数就是真正的延时的具体时间。
这里要特别注意的是,delta参数是“纳秒!”,就是说,延时1秒的话,delta应该是“1000000000”=。=,太长了,所以理所当然系统提供了常量,如下:
1
2
3
#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
关键词解释:
NSEC:纳秒。
USEC:微妙。
SEC:秒
PER:每
所以:
NSEC_PER_SEC,每秒有多少纳秒。
USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)
NSEC_PER_USEC,每毫秒有多少纳秒。
所以,延时1秒可以写成如下几种:
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
最后一个“USEC_PER_SEC * NSEC_PER_USEC”,翻译过来就是“每秒的毫秒数乘以每毫秒的纳秒数”,也就是“每秒的纳秒数”,所以,延时500毫秒之类的,也就不难了吧~
dispatch_suspend != 立即停止队列的运行
dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block,看如下例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一个block,延时5秒打印。
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds...");
});
//提交第二个block,也是延时5秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds again...");
});
//延时一秒
NSLog(@"sleep 1 second...");
[NSThread sleepForTimeInterval:1];
//挂起队列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延时10秒
NSLog(@"sleep 10 second...");
[NSThread sleepForTimeInterval:10];
//恢复队列
NSLog(@"resume...");
dispatch_resume(queue);
运行结果如下:
1
2
3
4
5
6
2015-04-01 00:32:09.903 GCDTest[47201:1883834] sleep 1 second...
2015-04-01 00:32:10.910 GCDTest[47201:1883834] suspend...
2015-04-01 00:32:10.910 GCDTest[47201:1883834] sleep 10 second...
2015-04-01 00:32:14.908 GCDTest[47201:1883856] After 5 seconds...
2015-04-01 00:32:20.911 GCDTest[47201:1883834] resume...
2015-04-01 00:32:25.912 GCDTest[47201:1883856] After 5 seconds again...
可知,在dispatch_suspend挂起队列后,第一个block还是在运行,并且正常输出。
结合文档,我们可以得知,dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。
所以下次想暂停正在队列上运行的block时,还是不要用dispatch_suspend了吧~
“同步”的dispatch_apply
dispatch_apply的作用是在一个队列(串行或并行)上“运行”多次block,其实就是简化了用循环去向队列依次添加block任务。但是我个人觉得这个函数就是个“坑”,先看看如下代码运行结果:
1
2
3
4
5
6
7
8
//创建异步串行队列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//运行block3次
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
});
//打印信息
NSLog(@"After apply");
运行的结果是:
1
2
3
4
2015-04-01 00:55:40.854 GCDTest[47402:1893289] apply loop: 0
2015-04-01 00:55:40.856 GCDTest[47402:1893289] apply loop: 1
2015-04-01 00:55:40.856 GCDTest[47402:1893289] apply loop: 2
2015-04-01 00:55:40.856 GCDTest[47402:1893289] After apply
看,明明是提交到异步的队列去运行,但是“After apply”居然在apply后打印,也就是说,dispatch_apply将外面的线程(main线程)“阻塞”了!
查看官方文档,dispatch_apply确实会“等待”其所有的循环运行完毕才往下执行=。=,看来要小心使用了。
避免死锁!
dispatch_sync导致的死锁
涉及到多线程的时候,不可避免的就会有“死锁”这个问题,在使用GCD时,往往一不小心,就可能造成死锁,看看下面的“死锁”例子:
1
2
3
4
//在main线程使用“同步”方法提交Block,必定会死锁。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"I am block...");
});
你可能会说,这么低级的错误,我怎么会犯,那么,看看下面的:
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)updateUI1 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Update ui 1");
//死锁!
[self updateUI2];
});
}
- (void)updateUI2 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Update ui 2");
});
}
在你不注意的时候,嵌套调用可能就会造成死锁!所以为了“世界和平”=。=,我们还是少用dispatch_sync吧。
dispatch_apply导致的死锁!
啥,dispatch_apply导致的死锁?。。。是的,前一节讲到,dispatch_apply会等循环执行完成,这不就差不多是阻塞了吗。看如下例子:
1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
//再来一个dispatch_apply!死锁!
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"apply loop inside %zu", j);
});
});
这端代码只会输出“apply loop: 1”。。。就没有然后了=。=
所以,一定要避免dispatch_apply的嵌套调用。
灵活使用dispatch_group
很多时候我们需要等待一系列任务(block)执行完成,然后再做一些收尾的工作。如果是有序的任务,可以分步骤完成的,直接使用串行队列就行。但是如果是一系列并行执行的任务呢?这个时候,就需要dispatch_group帮忙了~总的来说,dispatch_group的使用分如下几步:
创建dispatch_group_t
添加任务(block)
添加结束任务(如清理操作、通知UI等)
下面着重讲讲在后面两步。
添加任务
添加任务可以分为以下两种情况:
自己创建队列:使用dispatch_group_async。
无法直接使用队列变量(如使用AFNetworking添加异步任务):使用dispatch_group_enter,dispatch_group_leave。
自己创建队列时,当然就用dispatch_group_async函数,简单有效,简单例子如下:
1
2
3
4
//省去创建group、queue代码。。。
dispatch_group_async(group, queue, ^{
//Do you work...
});
当你无法直接使用队列变量时,就无法使用dispatch_group_async了,下面以使用AFNetworking时的情况:
1
2
3
4
5
6
7
8
9
10
11
12
13
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//Enter group
dispatch_group_enter(group);
[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Deal with result...
//Leave group
dispatch_group_leave(group);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Deal with error...
//Leave group
dispatch_group_leave(group);
}];
//More request...
使用dispatch_group_enter,dispatch_group_leave就可以方便的将一系列网络请求“打包”起来~
添加结束任务
添加结束任务也可以分为两种情况,如下:
在当前线程阻塞的同步等待:dispatch_group_wait。
添加一个异步执行的任务作为结束任务:dispatch_group_notify
这两个比较简单,就不再贴代码了=。=
使用dispatch_barrier_async,dispatch_barrier_sync的注意事项
dispatch_barrier_async的作用就是向某个队列插入一个block,当目前正在执行的block运行完成后,阻塞这个block后面添加的block,只运行这个block直到完成,然后再继续后续的任务,有点“唯我独尊”的感觉=。=
值得注意的是:
dispatchbarrier\(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。
既然在串行队列上跟dispatch_(a)sync效果一样,那就要小心别死锁!
dispatch_set_context与dispatch_set_finalizer_f的配合使用
dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:
用C语言的malloc创建context数据。
用C++的new创建类对象。
用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。
以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用free、delete还是CF的CFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。
所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置“析构函数”,在这个函数里面释放内存,大致如下:
1
2
3
4
5
6
7
8
9
void cleanStaff(void *context) {
//释放context的内存!
//CFRelease(context);
//free(context);
//delete context;
}
...
//在队列创建后,设置其“析构函数”
dispatch_set_finalizer_f(queue, cleanStaff);
详细用法,请看我之前写的Blog为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权
总结
其实本文更像是总结了GCD中的“坑”=。=
至于经验,总结一条,就是使用任何技术,都要研究透彻,否则后患无穷啊~
发表评论
-
RunLoop
2015-06-10 11:38 532转载:http://www.cocoachina.com/io ... -
CABaseAnimation
2015-04-24 13:30 713转载:http://blog.csdn.net/iosevan ... -
Method Swizzling
2015-03-25 15:41 502转载:http://www.cocoachina.com/io ... -
NSOperation, NSOperationQueue
2015-03-23 14:55 504转载:http://greenchiu.github.io/b ... -
ios申请真机调试( xcode 5)详细解析
2015-03-20 14:28 383转载:http://my.oschina.net/u/1245 ... -
一个苹果证书怎么多次使用——导出p12文件
2015-03-20 14:27 1185转载:http://my.oschina.net/u/1245 ... -
iOS申请证书,Certificates, Identifiers &Profiles 简介
2015-03-20 14:26 1026转载:http://my.oschina.net/u/1245 ... -
GCD 深入理解
2015-03-19 15:30 750转载:http://www.cocoachina.com/io ... -
发布iOS应用(xcode5)到App Store(苹果商店) 详细解析
2015-03-17 15:22 383转载:http://my.oschina.net/u/1245 ... -
Objective-C Runtime
2015-03-13 16:52 403转载:http://www.cocoachina.com/io ... -
ScrollView 与 Autolayout
2015-03-04 15:45 427http://www.cocoachina.com/ios/2 ... -
初探 ios 8 Size Class
2015-02-13 10:12 570转载:http://blog.csdn.net/please ... -
ios 8通知中心快速回复
2015-02-12 11:32 443转载:http://www.cocoachina.com/i ... -
StoryBoard
2015-02-09 12:54 349storyboard 学习资料1 http://www. ... -
Autolayout
2015-02-03 14:36 466推荐的链接: ios 自动布局教程(1) http:/ ...
相关推荐
关于GCD的用法、注意事项、难点的总结代码。
GCD资源竞争Demo。注意,别运行imagegcd2.m,这是个反面教材。
深度图卷积图像去噪Diego Valsesia,Giulia Fracastoro和Enrico Magli撰写的论文“深度图卷积图像降噪”的代码。 BibTex针对期刊和会议版本的参考: @article{valsesia2020deep, title={Deep graph-convolutional ...
GCD Web应用程序 该文件的最新更新: 2020年8月 这是Grand Comics数据库的当前实现,托管在 (生产)和 (新功能的公共Beta版)上。 请参阅和的“以获取有关我们的工作方式和迄今为止所做的工作的更多详细信息。 该...
一个简单的基于 GCD 的 Swift HTTP 库。 这个项目是“纯”的 Swift/C,它不使用任何桥接的 Objective-C 类。 SwiftyHTTP 是关于如何将 Swift 与原始 C API 集成的演示。 更多的是窃取 Swift 编码的想法,而不是在...
GCD_课程_项目 Coursera“获取和清理数据”课程项目 R 脚本文件的名称:run_analysis.R 先决条件: 工作目录包含解压后的“getdata_projectfiles_UCI HAR Dataset.zip”文件,即“UCI HAR Dataset”目录及其所有子...
这段代码中,gcd函数使用欧几里德算法递归计算最大公约数。当两个数的余数为0时,较小的数即为最大公约数。lcm函数则通过先求得最大公约数,然后利用公式 LCM(a, b) = |a * b| / GCD(a, b) 计算最小公倍数。 请注意...
语法:GCD(number1,number2,...) 参数:Number1,number2, ...为1 到29 个数值,如果数值为非整数,则截尾取整。说明:如果该函数不存在,必须运行“安装”程序加载“分析工具库”。 实例:如果A1=16、A2=28、A3=...
获取和清理数据课程项目 回购内容 run_analysis.R - 此文件从 UCI 机器学习存储库(位于 )获取一组原始数据并应用几个转换为数据集以创建一个整洁的数据集以供... 注意:变量名称的附加说明位于代码手册(第 4 步)中
获取和清理数据课程项目 获取和清理数据课程的课程项目(约翰霍普金斯大学) ##This repo 包括以下文件: 这个 README.md 文件 一个名为 run_analysis.R 的 R 脚本 随附的 CodeBook.md 描述了变量... 注意:从实验中
CocoaheadsHBA 同步搜索一个 iOS 演示项目,展示了在输入 ... 请注意,显示结果所需时间的 200 毫秒差异是提供 poptime 的效果。执照所有代码都是在公共领域发布的,因此是免费的。归因代码由 CocoaHeads Bremen 的Hel
要注意的是,你应该先打开仪器电源,然后再打开操作软件,这时软件会自动寻找并匹配通讯端口,而先打开操作软件再打开仪器电源的话,则要手动去设置通讯端口,所以建议先打开仪器电源,再打开操作软件。
1.版本:matlab2021a,包含仿真操作录像,操作录像使用windows media player播放。 2.领域:dds直接数字频率合成器性能仿真 3.内容:基于MATLAB的dds直接数字频率合成器性能仿真,包括分析只有相位截断误差,只有...
相信一说到定时器, 我们使用最多的就是NSTimer 和 GCD 了, 还有另外一个高级的定时器 CADisplayLink;,下面将给大家详细介绍关于iOS定时器使用的相关内容,话不多说了,来一起看看详细的介绍吧。 一. NSTimer ...
请注意,只有本机Unix,Cygwin和WSL已经过正式测试。 如果任何其他系统存在问题,请报告问题。 设置完项目后,只需运行cmake . 然后make编译程序。 默认情况下,这将同时编译gcd和test_gcd可执行文件。 gcd是...
目标:block执行过程中,self不会释放;执行完可以释放。 最初 block中直接使用self会强引用。 self.myBlock = ^() { [self doSomething];...一般使用GCD或NSOperation时使用的内联block是不会出现循环
以上两种方式都是模拟任务block内为异步操作的情况,方式一先执行的dispatch_group_notify里的代码,后执行的dispatch_group_async里的...我们在使用dispatch_group时一般都是想异步执行任务,所以,一定要注意这个坑
计算两个数字的最大公约数(GCD) 计算两个数字的最小公倍数(LCM) 计算给定范围内的所有阿姆斯壮数字(请注意:阿姆斯壮数字(也称为自恋数字):一个n位数字,等于其数字的n次幂。)请参阅 计算给定范围内的...
体重记录仪-图片 主要特点: 核心数据 将图像添加到CoreData UIImagePickerController的线程 这是项目的扩展。 此扩展将全屏/缩略图图像添加到CoreData。...注意:这是XCode 7和Swift 2.0的最新信息
多线程概述 对于ios系统中的某个App来讲,是单进程多线程方式来工作。一般来说,使用多线程的好处是可以把程序...如果我们用GCD,可以使用dispatcg_barrier_async 来实现,如下: 注意这里的queue 不能是 global_queue