GCD使用的总结

GCD(Grand Central Dispatch)可以说是Mac、iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧。

1、首先说道GCD,必须先把单例说说

这一条算是“老生常谈”了,但我认为还是有必要强调一次,毕竟非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
   //单例代码
});
2、聊聊GCD什么时候开线程,什么时候不开线程,什么时候会死锁!

1、队列
GCD中分为串行队列(DISPATCH_QUEUE_SERIAL)和并发队列(DISPATCH_QUEUE_CONCURRENT);
2、同步执行or异步执行
dispatch_async 异步执行
dispatch_sync 同步执行
下面有一个表,讲的是什么时候开不开线程,看一下!

全局队列 串行队列 主队列
同步执行(sync) 没有开启线程,串行执行 没有开启线程,串行执行 会死锁
异步执行(async) 开启新线程,并发执行 开启新线程,并发执行 没有开启线程,串行执行
3、创建队列

dispatch_queue_create,创建队列用的,它的参数只有两个,原型如下:

dispatch_queue_t dispatch_queue_create ( const char *label, dispatch_queue_attr_t attr );

在网上的大部分教程里(甚至Apple自己的文档里),都是这么创建串行队列的:

dispatch_queue_t queue = dispatch_queue_create("LHQueue", NULL);

看,第二个参数传的是“NULL”。 但是dispatch_queue_attr_t类型是有已经定义好的常量的,所以我认为,为了更加的清晰、严谨,最好如下创建队列:

//串行队列
dispatch_queue_t queue = dispatch_queue_create("LHQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("LHQueue", DISPATCH_QUEUE_CONCURRENT);
  • 全局队列
* 调度任务的时候,多个线程,而且是顺序不固定
* 并发能力好
* 效率高,速度快,费电,费钱
  • 串行队列
* 调度任务,只有一条线程,顺序执行每一个任务
* 并发能力差
* 效率差,速度慢,省电,省钱
* 用户并不是什么时候都会希望快的!
4、创建多线程
//串行队列异步执行
dispatch_queue_t queue = dispatch_queue_create("LHQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
   //开启一条线程,并发执行
});

//并发队列异步执行
dispatch_queue_t queue = dispatch_queue_create("LHQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
   //开启线程,并发执行
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //同上。。。
    });

在开发中,回到主线程也可以用

[self performSelectorOnMainThread:@selector(clearCacheSuccess) withObject:nil waitUntilDone:YES];
});

dispatch_async(dispatch_get_main_queue(), ^{
    //主线程
});

5、GCD组的使用(Dispatch Groud)

dispatch_group_create();
dispatch_group_async();
dispatch_group_notify();
dispatch_group_wait();

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后才通知界面说完成。该组中是并发执行的。例如:

- (void)gcdGroup{ 
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, ^{
   [NSThread sleepForTimeInterval:1]; NSLog(@"group1"); 
}); 
dispatch_group_async(group, queue, ^{ 
  [NSThread sleepForTimeInterval:2]; NSLog(@"group2");
 }); 
dispatch_group_async(group, queue, ^{ 
  [NSThread sleepForTimeInterval:3]; NSLog(@"group3"); 
}); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
  NSLog(@"updateUi");
 }); 

//打印顺序
2016-07-14 23:33:09.593 GCDDemo[959:49260] group1
2016-07-14 23:33:10.593 GCDDemo[959:49262] group2
2016-07-14 23:33:11.594 GCDDemo[959:49261] group3
2016-07-14 23:33:11.595 GCDDemo[959:49222] updateUi
6、dispatch_after

dispatch_after是延迟提交,不是延迟运行,先看看官方文档的说明:Enqueue a block for execution at the specified time.
Enqueue,就是入队,指的就是将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行!。

看看如下代码示例:

//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);

//立即打印一条信息
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...");
});
//输出
2016-07-14 20:57:27.122 GCDTest[45633:1812016] Begin add block...
2016-07-14 20:57:37.127 GCDTest[45633:1812041] First block done...
2016-07-14 20:57:37.127 GCDTest[45633:1812041] After...

//从结果也验证了,dispatch_after只是延时提交block,并不是延时后立即执行。
//所以想用dispatch_after精确控制运行状态的朋友可要注意了~
7、死锁

dispatch_sync导致的死锁
涉及到多线程的时候,不可避免的就会有“死锁”这个问题,在使用GCD时,往往一不小心,就可能造成死锁,看看下面的“死锁”例子:

dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"I am block...");
});

你可能会说,这么低级的错误,我怎么会犯,那么,看看下面的:

- (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!!!

8、正确创建dispatch_time_t

用dispatch_after的时候就会用到dispatch_time_t变量,但是如何创建合适的时间呢?答案就是用dispatch_time函数,其原型如下:

dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );

第一个参数一般是DISPATCH_TIME_NOW,表示从现在开始。
那么第二个参数就是真正的延时的具体时间。
这里要特别注意的是,delta参数是“纳秒!”,就是说,延时1秒的话,delta应该是“1000000000”=。=,太长了,所以理所当然系统提供了常量,如下:

#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_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)
(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(),
^{
      //这个就是延迟一秒
   });
9、dispatch_suspend/dispatch_resume

当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。例如演算结果被Block截获时,一些处理会对这个演算结果造成影响; 在这种情况下,只要挂起Dispatch Queue即可。当可以执行时再回复。

//dispatch_suspend函数挂起指定的Dispatch Queue
dispatch_suspend(queue);
//dispatch_resume函数恢复指定的Dispatch Queue
dispatch_resume(queue);

这些函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行。而恢复则使得这些处理能够继续执行。

有不对的地方,多指正。。。谢谢!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容