重新回顾、学习GCD、Block。先贴出一篇不错的讲解GCD基础使用的文章
原文地址:http://blog.csdn.net/aolan1108/article/details/17283415
做了2年的ios开发,很想静下心来想想,做一些总结,但是苦于生活和工作方面的种种原因,一直没能如愿。今天终于下定决心,把自己所学所想记录下来,方便以后查看,同时可供大家分享。
就像之前说的,做了两年的ios开发,只知道不断的copy代码,如果你要真的问我GCD是神马东东,我一时半会也说不清楚。这里我要为自己生存在互联网时代感觉到庆幸,我在网上翻阅了很多资料,大都不太完整,在这些资料中,我不断筛选,终于把GCD给整明白了。我不喜欢说官方的话,说一个东西,从他的定义开始,这个比较俗套,如果大家真的不明白GCD是啥意思,可以自己找去。 队列种类: 一、主线程的Main queue,通过dispatch_get_main_queue获取。 二、并行队列global dispatch queue,通过dispatch_get_global_queue获取,由系统创建(不需要开发人员去创建)三个不同优先级的dispatch queue。并行队列的执行顺序与其加入队列的顺序相同。 三、串行队列serial queues一般用于按顺序同步访问,可创建任意数量的串行队列,各个串行队列之间是并发的。一般用dispatch_queue_create来进行创建,非arc的情况下需要用户手动来释放队列,可能会有人说,既然队列也是一种对象,可以创建和释放,那一定会有引用计数器了,是的,可以使用函数dispatch_retain和dispatch_release来增加或者减少引用计数器。 提交job: 在举例子给大家看之前,我还想说说两种提交job的方式:dispatch_async和dispatch_sync,分别是异步执行和同步执行,两者之前的区别在于,前者在把任务提交到队列执行不会造成阻塞,而后者后面的代码块需要等到队列中的任务执行完成后才可以执行。 下面是例子 例子一,我们可以看到首先执行的是第二条打印语句,这说明这条语句没有被阻塞。 [objc] view plaincopy//主线程异步执行 -(void)action1{ dispatch_async(dispatch_get_main_queue(), ^{ for (int i = 0; i< 5; i++) NSLog(@"主线程异步执行===========%d",i); }); NSLog(@"=================主线程异步执行"); }
2013-12-09 14:36:20.863 TestGCD[872:a0b] =================主线程异步执行
2013-12-09 14:36:20.864 TestGCD[872:a0b] 主线程异步执行===========0
2013-12-09 14:36:20.865 TestGCD[872:a0b] 主线程异步执行===========1
2013-12-09 14:36:20.865 TestGCD[872:a0b] 主线程异步执行===========2
2013-12-09 14:36:20.865 TestGCD[872:a0b] 主线程异步执行===========3
2013-12-09 14:36:20.866 TestGCD[872:a0b] 主线程异步执行===========4
例子二,会有人问,既然主线程可以异步执行,那么主线程也可以同步执行喽。的确是可以的,但是不能直接在action1里面将dispatch_async改为dispatch_sync,这样修改是没有意义的,大家可以试试,里面的任务根本不会执行。 [objc] view plaincopy//主线程同步执行 -(void)action2{ dispatch_async(dispatch_get_global_queue(0, 0), ^{ for (int i = 0; i< 3; i++) NSLog(@"并发线程异步执行===========%d",i); dispatch_sync(dispatch_get_main_queue(), ^{ for (int i = 0; i< 3; i++) NSLog(@"主线程同步执行===========%d",i); }); NSLog(@"===========主线程执行完毕"); }); NSLog(@"===========并发线程可能正在执行"); }
打印结果:
2013-12-09 15:22:22.352 TestGCD[1269:a0b] ===========并发线程可能正在执行
2013-12-09 15:22:22.352 TestGCD[1269:1403] 并发线程异步执行===========0
2013-12-09 15:22:22.355 TestGCD[1269:1403] 并发线程异步执行===========1
2013-12-09 15:22:22.356 TestGCD[1269:1403] 并发线程异步执行===========2
2013-12-09 15:22:22.357 TestGCD[1269:a0b] 主线程同步执行===========0
2013-12-09 15:22:22.358 TestGCD[1269:a0b] 主线程同步执行===========1
2013-12-09 15:22:22.358 TestGCD[1269:a0b] 主线程同步执行===========2
2013-12-09 15:22:22.359 TestGCD[1269:1403] ===========主线程执行完毕
从结果中我们仔细理解,就会比较深刻,首先我们看到打印语句4最先执行(有可能不是最先执行,这个是由系统决定的),说明我们提交到并发队列dispatch_get_global_queue的执行方式是异步的;其次打印语句2是在打印语句1执行完才开始执行,这说明dispatch_get_global_queue虽然是并发队列,但是其内部的任务执行顺序是串行的;最后,我们看到打印语句3是在打印语句2执行完成后再执行,说明主线程同步执行是阻塞的,我们通常会将UI的刷新用同步方式放到主线程中去操作,当然这种操作的时间一般都比较短,以至于用户几乎无法察觉。
例子三,其实GCD的操作真正的意义当然不在于在主线程中做一些操作,在很多时候,我们都会用到后台线程,如果你的iphone是多核的cpu,那么恭喜你,你可以有流畅的操作体验,即使不是多核,你的操作体验也相对流畅,因为你可以将需要大量时间才能执行完成的任务放到后台线程中去执行,用户就不会有死机感,譬如说图片加载、网络请求、复杂逻辑的数据解析等等。
//后台异步执行
-(void)action3{ dispatch_async(dispatch_get_global_queue(0, 0), ^{ for (int i = 0; i< 10000; i++) NSLog(@"=================%d",i); }); NSLog(@"=================后台异步执行"); }
你可以迅速点击两次按钮,即连续执行两次action3方法,在这里,我们截取部分打印结果:
2013-12-09 15:51:30.864 TestGCD[1350:3007] =================0
2013-12-09 15:51:30.864 TestGCD[1350:a0b] =================后台异步执行
2013-12-09 15:51:30.864 TestGCD[1350:3007] =================1
2013-12-09 15:51:30.865 TestGCD[1350:3007] =================2
2013-12-09 15:51:30.865 TestGCD[1350:3007] =================3
......
2013-12-09 15:51:31.007 TestGCD[1350:3007] =================600
2013-12-09 15:51:31.007 TestGCD[1350:3007] =================601
2013-12-09 15:51:31.007 TestGCD[1350:a0b] =================后台异步执行
2013-12-09 15:51:31.007 TestGCD[1350:3007] =================602
2013-12-09 15:51:31.008 TestGCD[1350:3007] =================603
2013-12-09 15:51:31.007 TestGCD[1350:473] =================0
2013-12-09 15:51:31.008 TestGCD[1350:473] =================1
2013-12-09 15:51:31.008 TestGCD[1350:473] =================2
2013-12-09 15:51:31.008 TestGCD[1350:3007] =================604
2013-12-09 15:51:31.008 TestGCD[1350:473] =================3
2013-12-09 15:51:31.008 TestGCD[1350:3007] =================605
......
我们看到由于action3被执行了两次,在点击第一次后,屏幕仍然接受点击时间,说明主线程没有被阻塞,用户体验仍然很流畅。两次点击实际上是提交了两个任务到dispatch_get_global_queue队列上,第一次任务并没有执行完成,第二次任务就开始执行,说明dispatch_get_global_queue是并行队列,即任务块与任务块之间是并发执行的。
例子四,肯定会有小伙伴们说后台既然可以异步执行,那么应该也可以同步执行喽,是的,你是对的,但是在实际的开发过程中没有什么意义,我们会发现改方法会产生阻塞,但是却不能用来刷新UI,也没有体现多核处理器的优势。
//后台同步执行 -(void)action4{ dispatch_sync(dispatch_get_global_queue(0, 0), ^{ for (int i = 0; i< 10000; i++) NSLog(@"=================%d",i); }); NSLog(@"=================后台同步执行"); }
我们也是连续点击两次按钮,执行两次action4方法,我们看到的打印结果让我们很惊讶,可能会有人说,dispatch_get_global_queue队列不是并发队列吗,怎么执行的结果是这样的。在这里我需要解释下,由于提交给全局队列的执行方式是同步的,这里实际上是产生了阻塞,即必须是该任务完成后才能执行下面的任务,我们看到打印语句2执行的位置就知道了。所以同步一般在刷新UI界面或者处理共享数据的时候使用,而且任务的处理时间不能太长,会影响用户体验。
2013-12-09 16:04:35.967 TestGCD[1385:a0b] =================0
2013-12-09 16:04:35.969 TestGCD[1385:a0b] =================1
2013-12-09 16:04:35.969 TestGCD[1385:a0b] =================2
......2013-12-09 16:04:40.645 TestGCD[1385:a0b] =================9997
2013-12-09 16:04:40.645 TestGCD[1385:a0b] =================9998
2013-12-09 16:04:40.646 TestGCD[1385:a0b] =================9999
2013-12-09 16:04:40.646 TestGCD[1385:a0b] =================后台同步执行
2013-12-09 16:04:40.647 TestGCD[1385:a0b] =================0
2013-12-09 16:04:40.647 TestGCD[1385:a0b] =================1
2013-12-09 16:04:40.648 TestGCD[1385:a0b] =================2
2013-12-09 16:04:40.648 TestGCD[1385:a0b] =================3
......
2013-12-09 16:04:45.218 TestGCD[1385:a0b] =================9998
2013-12-09 16:04:45.218 TestGCD[1385:a0b] =================9999
2013-12-09 16:04:45.218 TestGCD[1385:a0b] =================后台同步执行
例子五,开发人员可以自己定义一个队列用于执行后台线程,同全局队列使用方法基本类似,在非arc的情况下,需要开发人员手动释放。
//自定义dispatch_queue_t -(void)action5{ dispatch_queue_t urls_queue = dispatch_queue_create("myQueue", NULL); dispatch_async(urls_queue, ^{ for (int i = 0; i< 5; i++) NSLog(@"自定义dispatch_queue_t===========%d",i); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"同主线程交互==========="); }); }); dispatch_release(urls_queue); //arc不要 }
打印结果:
2013-12-09 16:43:52.416 TestGCD[1464:1417] 自定义dispatch_queue_t===========0
2013-12-09 16:43:52.417 TestGCD[1464:1417] 自定义dispatch_queue_t===========1
2013-12-09 16:43:52.418 TestGCD[1464:1417] 自定义dispatch_queue_t===========2
2013-12-09 16:43:52.419 TestGCD[1464:1417] 自定义dispatch_queue_t===========3
2013-12-09 16:43:52.420 TestGCD[1464:1417] 自定义dispatch_queue_t===========4
2013-12-09 16:43:52.420 TestGCD[1464:a0b] 同主线程交互===========
例子六,除此之外,调度方式还有延迟执行。
//延迟3秒执行 -(void)action6{ double delayInSeconds = 3.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(@"延迟3秒执行==========="); }); }
打印结果:
2013-12-09 16:48:58.473 TestGCD[1484:a0b] 延迟3秒执行===========
例子七,一次性执行,主要用于创建单例对象。在ios4.0以前,我们创建单例会用到互斥锁@synchronized来确保其他线程没有对self对象进行修改,一般在共用变量的时候使用。
+ (NetworkManager *)getNetworkInstance{ @synchronized(self){ if (nil == network) network = [[NetworkManager alloc] init]; } return network; }
ios4.0之后,我们可以用GCD创建单例,下面的GCD语法只会执行一次,代码如下:
+(UserManager *)sharedManager{ static UserManager *_manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _manager = [[UserManager alloc]init]; }); return _manager; }
该方法的好处有:一、线程安全;二、很好满足静态分析器的要求;三、兼容ARC;四、仅需要少量代码。
例子八,合并汇总结果,dispatch_group_t队列非常好用,当我们有多个异步执行的队列在执行,我们还有一个任务需要用到这多个异步执行队列执行的结果时,我们就会用到dispatch_group_t,废话不多说,直接上代码:
//合并汇总结果 -(void)action8{ dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ for (int i = 0; i < 5; i++) NSLog(@"并行执行的线程一=============%d",i); }); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ for (int i = 0; i < 5; i++) NSLog(@"并行执行的线程二=============%d",i); }); dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{ NSLog(@"汇总结果==========="); }); dispatch_release(group); }
打印结果:
2013-12-10 11:48:39.139 TestGCD[699:1403] 并行执行的线程一=============0
2013-12-10 11:48:39.139 TestGCD[699:3807] 并行执行的线程二=============0
2013-12-10 11:48:39.143 TestGCD[699:1403] 并行执行的线程一=============1
2013-12-10 11:48:39.143 TestGCD[699:3807] 并行执行的线程二=============1
2013-12-10 11:48:39.144 TestGCD[699:1403] 并行执行的线程一=============2
2013-12-10 11:48:39.144 TestGCD[699:3807] 并行执行的线程二=============2
2013-12-10 11:48:39.145 TestGCD[699:1403] 并行执行的线程一=============3
2013-12-10 11:48:39.145 TestGCD[699:3807] 并行执行的线程二=============3
2013-12-10 11:48:39.146 TestGCD[699:3807] 并行执行的线程二=============4
2013-12-10 11:48:39.146 TestGCD[699:1403] 并行执行的线程一=============4
2013-12-10 11:48:39.147 TestGCD[699:3807] 汇总结果===========
我们看到第三条打印语句是在前两条打印语句执行之后才执行。
这也是我综合了官方文档和网上的很多资料总结出来的,并且用实际的代码调试出来的结果,希望能给大家一些帮助,对于其中的不足,欢迎大家给出不同意见。
参考:http://www.cnblogs.com/pure/archive/2013/03/31/2977420.html