什么是GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。该方法在Mac OS X 10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。

GCD和block的配合使用,可以方便地进行多线程编程。

应用举例

让我们来看一个编程场景。我们要在iPhone上做一个下载网页的功能,该功能非常简单,就是在iPhone上放置一个按钮,点击该按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成之后,将内容加载到界面上的一个文本控件中。

不用GCD前

虽然功能简单,但是我们必须把下载过程放到后台线程中,否则会阻塞UI线程显示。所以,如果不用GCD, 我们需要写如下3个方法:

  • someClick 方法是点击按钮后的代码,可以看到我们用NSInvocationOperation建了一个后台线程,并且放到NSOperationQueue中。后台线程执行download方法。
  • download 方法处理下载网页的逻辑。下载完成后用performSelectorOnMainThread执行download_completed 方法。
  • download_completed 进行clear up的工作,并把下载的内容显示到文本控件中。

这3个方法的代码如下。可以看到,虽然 开始下载 –> 下载中 –> 下载完成 这3个步骤是整个功能的三步。但是它们却被切分成了3块。他们之间因为是3个方法,所以还需要传递数据参数。如果是复杂的应用,数据参数很可能就不象本例子中的NSString那么简单了,另外,下载可能放到Model的类中来做,而界面的控制放到View Controller层来做,这使得本来就分开的代码变得更加散落。代码的可读性大大降低。

  1. static NSOperationQueue * queue;
  2.  
  3. - (IBAction)someClick:(id)sender {
  4. self.indicator.hidden = NO;
  5. [self.indicator startAnimating];
  6. queue = [[NSOperationQueue alloc] init];
  7. NSInvocationOperation * op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil] autorelease];
  8. [queue addOperation:op];
  9. }
  10.  
  11. - (void)download {
  12. NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];
  13. NSError * error;
  14. NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
  15. if (data != nil) {
  16. [self performSelectorOnMainThread:@selector(download_completed:) withObject:data waitUntilDone:NO];
  17. } else {
  18. NSLog(@"error when download:%@", error);
  19. [queue release];
  20. }
  21. }
  22.  
  23. - (void) download_completed:(NSString *) data {
  24. NSLog(@"call back");
  25. [self.indicator stopAnimating];
  26. self.indicator.hidden = YES;
  27. self.content.text = data;
  28. [queue release];
  29. }

使用GCD后

如果使用GCD,以上3个方法都可以放到一起,如下所示:

  1. // 原代码块一
  2. self.indicator.hidden = NO;
  3. [self.indicator startAnimating];
  4. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
  5. // 原代码块二
  6. NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];
  7. NSError * error;
  8. NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
  9. if (data != nil) {
  10. // 原代码块三
  11. dispatch_async(dispatch_get_main_queue(), ^{
  12. [self.indicator stopAnimating];
  13. self.indicator.hidden = YES;
  14. self.content.text = data;
  15. });
  16. } else {
  17. NSLog(@"error when download:%@", error);
  18. }
  19. });

首先我们可以看到,代码变短了。因为少了原来3个方法的定义,也少了相互之间需要传递的变量的封装。

另外,代码变清楚了,虽然是异步的代码,但是它们被GCD合理的整合在一起,逻辑非常清晰。如果应用上MVC模式,我们也可以将View Controller层的回调函数用GCD的方式传递给Modal层,这相比以前用@selector的方式,代码的逻辑关系会更加清楚。

block的定义

block的定义有点象函数指针,差别是用 ^ 替代了函数指针的 * 号,如下所示:

  1. // 申明变量
  2. (void) (^loggerBlock)(void);
  3. // 定义
  4.  
  5. loggerBlock = ^{
  6. NSLog(@"Hello world");
  7. };
  8. // 调用
  9. loggerBlock();

但是大多数时候,我们通常使用内联的方式来定义block,即将它的程序块写在调用的函数里面,例如这样:

  1. dispatch_async(dispatch_get_global_queue(, ), ^{
  2. // something
  3. });

从上面大家可以看出,block有如下特点:

  1. 程序块可以在代码中以内联的方式来定义。
  2. 程序块可以访问在创建它的范围内的可用的变量。

系统提供的dispatch方法

为了方便地使用GCD,苹果提供了一些方法方便我们将block放在主线程 或 后台线程执行,或者延后执行。使用的例子如下:

  1. // 后台执行:
  2. dispatch_async(dispatch_get_global_queue(, ), ^{
  3. // something
  4. });
  5. // 主线程执行:
  6. dispatch_async(dispatch_get_main_queue(), ^{
  7. // something
  8. });
  9. // 一次性执行:
  10. static dispatch_once_t onceToken;
  11. dispatch_once(&onceToken, ^{
  12. // code to be executed once
  13. });
  14. // 延迟2秒执行:
  15. double delayInSeconds = 2.0;
  16. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
  17. dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  18. // code to be executed on the main queue after delay
  19. });

dispatch_queue_t 也可以自己定义,如要要自定义queue,可以用dispatch_queue_create方法,示例如下:

  1. dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
  2. dispatch_async(urls_queue, ^{
  3. // your code
  4. });
  5. dispatch_release(urls_queue);

另外,GCD还有一些高级用法,例如让后台2个线程并行执行,然后等2个线程都结束后,再汇总执行结果。这个可以用dispatch_group, dispatch_group_async 和 dispatch_group_notify来实现,示例如下:

  1. dispatch_group_t group = dispatch_group_create();
  2. dispatch_group_async(group, dispatch_get_global_queue(,), ^{
  3. // 并行执行的线程一
  4. });
  5. dispatch_group_async(group, dispatch_get_global_queue(,), ^{
  6. // 并行执行的线程二
  7. });
  8. dispatch_group_notify(group, dispatch_get_global_queue(,), ^{
  9. // 汇总结果
  10. });

修改block之外的变量

默认情况下,在程序块中访问的外部变量是复制过去的,即写操作不对原变量生效。但是你可以加上 __block来让其写操作生效,示例代码如下:

  1. __block int a = ;
  2. void (^foo)(void) = ^{
  3. a = ;
  4. }
  5. foo();
  6. // 这里,a的值被修改为1

后台运行

使用block的另一个用处是可以让程序在后台较长久的运行。在以前,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是应用可以调用UIApplication的beginBackgroundTaskWithExpirationHandler方法,让app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

让程序在后台长久运行的示例代码如下:

  1. // AppDelegate.h文件
  2. @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;
  3.  
  4. // AppDelegate.m文件
  5. - (void)applicationDidEnterBackground:(UIApplication *)application
  6. {
  7. [self beingBackgroundUpdateTask];
  8. // 在这里加上你需要长久运行的代码
  9. [self endBackgroundUpdateTask];
  10. }
  11.  
  12. - (void)beingBackgroundUpdateTask
  13. {
  14. self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
  15. [self endBackgroundUpdateTask];
  16. }];
  17. }
  18.  
  19. - (void)endBackgroundUpdateTask
  20. {
  21. [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
  22. self.backgroundUpdateTask = UIBackgroundTaskInvalid;
  23. }

总结

总体来说,GCD能够极大地方便开发者进行多线程编程。大家应该尽量使用GCD来处理后台线程和UI线程的交互。

转载:http://blog.devtang.com/blog/2012/02/22/use-gcd/

IOS开发使用GCD后台运行的更多相关文章

  1. ios开发多线程--GCD

    引言 虽然GCD使用很广,而且在面试时也经常问与GCD相关的问题,但是我相信深入理解关于GCD知识的人肯定不多,大部分都是人云亦云,只是使用过GCD完成一些很简单的功能.当然,使用GCD完成一些简单的 ...

  2. iOS保持App真后台运行

    https://www.jianshu.com/p/d466f2da0d33 在我看来,苹果系统与安卓系统最直观的区别就是后台处理方式了吧,安卓手机一旦开启了很多app放到后台,即使前台什么也不做,就 ...

  3. iOS开发 底层抛析运行循环—— RunLoop

    http://blog.csdn.net/zc639143029/article/details/50012527 一.RunLoop基本概念 概念:程序的运行循环,通俗的来说就是跑圈. 1. 基本作 ...

  4. 原创翻译:iOS 应用程序 蓝牙后台运行

    默认情况下,普通的CoreBluetooth 任务中的大多数,无论是Central还是peripheral ,在后台或者挂起状况下都是无法进行的.也就是说,你可以通过宣布你的应用程序支持后台处理模式来 ...

  5. Delphi IOS 蓝牙锁屏后台运行

    Delphi IOS 后台运行 同样的程序,编译成android,锁屏后继续运行正常,蓝牙通讯正常,但在IOS下锁屏后程序的蓝牙就中断通讯了? IOS的机制就是这样,锁屏就关闭了. 音乐播放器是怎么做 ...

  6. IOS开发 多线程GCD

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispat ...

  7. iOS开发——高级特性&Runtime运行时特性详解

    Runtime运行时特性详解 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机.主要内容如下: 引言 ...

  8. iOS开发中GCD在多线程方面的理解

    GCD为Grand Central Dispatch的缩写. Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法.在Mac OS X 10.6雪豹中 ...

  9. ios开发:GCD多线程

    ios有三种多线程编程技术,分别是NSThread,Cocoa NSOperation和GCD,GCD全称Grand Central Dispatch 是Apple开发的一个多核编程的解决方法,在iO ...

随机推荐

  1. @component的注解

    1.@controller 控制器(注入服务) 2.@service 服务(注入dao) 3.@repository dao(实现dao访问) 4.@component (把普通pojo实例化到spr ...

  2. (转)C#自制Web 服务器开发:用C#开发自己的Web服务器

    当输入:127.0.0.1:5050 GET / HTTP/1.1 Accept: text/html, application/xhtml+xml, */* Accept-Language: zh- ...

  3. 解读:hadoop压缩格式

    Hadoop中用得比较多的4种压缩格式:lzo,gzip,snappy,bzip2.它们的优缺点和应用场景如下: 1). gzip压缩 优点:压缩率比较高,而且压缩/解压速度也比较快:hadoop本身 ...

  4. SaltStack部署服务及配置管理apache+php-第二篇

    实验目标 1.使用SaltStack部署apache和php, 2.使用salt管理httpd.conf配置文件配置访问info.php使用账户密码 3.在salt里面增加对conf.d目录进行配置管 ...

  5. Html基本用法

    hmtl hyper text markup language  超文本标记语言 超文本:超越一般的文本 变色 加粗 变大 甚至设置超链接 标记:浏览器已经定义好的 一些由尖括号括起来的特殊符号 语言 ...

  6. Oracle邮件推送函数

    CREATE OR REPLACE PROCEDURE PROCSENDEMAIL ( P_TXT VARCHAR2, P_SUB VARCHAR2, P_SENDOR VARCHAR2, P_REC ...

  7. java入门了解01

    http://www.oracle.com/technetwork/java/javase/downloads/index.html dos命令大全:http://www.zou114.com/dos ...

  8. centos7.1 从源码升级安装Python3.5.2

    http://blog.csdn.net/tengyunjiawu_com/article/details/53535153 centos7.1 从源码升级安装Python3.5.2(我写的,请大家度 ...

  9. How to implement multiple constructor with different parameters in Scala

    Using scala is just another road, and it just like we fall in love again, but there is some pain you ...

  10. @DataProvider的应用

    代码中经常有一些数据需要维护,但是每次都写在一个class或者methods中,维护起来是个麻烦事: 这里引入@DataProvider的思想,建一个DataProvider的方法,让我们需要维护的数 ...