什么是 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 层来做,这使得本来就分开的代码变得更加散落。代码的可读性大大降低。

 static NSOperationQueue * queue;

 - (IBAction)someClick:(id)sender {
self.indicator.hidden = NO;
[self.indicator startAnimating];
queue = [[NSOperationQueue alloc] init];
NSInvocationOperation * op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil] autorelease];
[queue addOperation:op];
} - (void)download {
NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];
NSError * error;
NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (data != nil) {
[self performSelectorOnMainThread:@selector(download_completed:) withObject:data waitUntilDone:NO];
} else {
NSLog(@"error when download:%@", error);
[queue release];
}
} - (void) download_completed:(NSString *) data {
NSLog(@"call back");
[self.indicator stopAnimating];
self.indicator.hidden = YES;
self.content.text = data;
[queue release];
}

使用 GCD 后

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

 // 原代码块一
self.indicator.hidden = NO;
[self.indicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 原代码块二
NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];
NSError * error;
NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (data != nil) {
// 原代码块三
dispatch_async(dispatch_get_main_queue(), ^{
[self.indicator stopAnimating];
self.indicator.hidden = YES;
self.content.text = data;
});
} else {
NSLog(@"error when download:%@", error);
}
});

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

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

block 的定义

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

  // 申明变量
(void) (^loggerBlock)(void);
// 定义 loggerBlock = ^{
NSLog(@"Hello world");
};
// 调用
loggerBlock();

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

 dispatch_async(dispatch_get_global_queue(, ), ^{
// something
});

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

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

系统提供的 dispatch 方法

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

 //  后台执行:
dispatch_async(dispatch_get_global_queue(, ), ^{
// something
});
// 主线程执行:
dispatch_async(dispatch_get_main_queue(), ^{
// something
});
// 一次性执行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
// 延迟 2 秒执行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});

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

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

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

  dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(,), ^{
// 并行执行的线程一
});
dispatch_group_async(group, dispatch_get_global_queue(,), ^{
// 并行执行的线程二
});
dispatch_group_notify(group, dispatch_get_global_queue(,), ^{
// 汇总结果
});

修改 block 之外的变量

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

  __block int a = ;
void (^foo)(void) = ^{
a = ;
}
foo();
// 这里,a 的值被修改为 1

后台运行

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

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

 // AppDelegate.h 文件
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask; // AppDelegate.m 文件
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self beingBackgroundUpdateTask];
// 在这里加上你需要长久运行的代码
[self endBackgroundUpdateTask];
} - (void)beingBackgroundUpdateTask
{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
} - (void)endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}

 

GCD的使用的更多相关文章

  1. Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...

  2. iOS 多线程之GCD的使用

    在iOS开发中,遇到耗时操作,我们经常用到多线程技术.Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法,只需定义想要执行的任务,然后添加到适当的调度队列 ...

  3. 【swift】BlockOperation和GCD实用代码块

    //BlockOperation // // ViewController.swift import UIKit class ViewController: UIViewController { @I ...

  4. 修改版: 小伙,多线程(GCD)看我就够了,骗你没好处!

    多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系 ...

  5. GCD的相关函数使用

    GCD 是iOS多线程实现方案之一,非常常用 英文翻译过来就是伟大的中枢调度器,也有人戏称为是牛逼的中枢调度器 是苹果公司为多核的并行运算提出的解决方案 1.一次性函数 dispatch_once 顾 ...

  6. hdu1695 GCD(莫比乌斯反演)

    题意:求(1,b)区间和(1,d)区间里面gcd(x, y) = k的数的对数(1<=x<=b , 1<= y <= d). 知识点: 莫比乌斯反演/*12*/ 线性筛求莫比乌 ...

  7. hdu2588 GCD (欧拉函数)

    GCD 题意:输入N,M(2<=N<=1000000000, 1<=M<=N), 设1<=X<=N,求使gcd(X,N)>=M的X的个数.  (文末有题) 知 ...

  8. BZOJ 2820: YY的GCD [莫比乌斯反演]【学习笔记】

    2820: YY的GCD Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1624  Solved: 853[Submit][Status][Discu ...

  9. BZOJ 2818: Gcd [欧拉函数 质数 线性筛]【学习笔记】

    2818: Gcd Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4436  Solved: 1957[Submit][Status][Discuss ...

  10. GCD总结

    //用block只有两种:同步执行/异步执行(参数1:队列;参数二:任务) dispatch_async(dispatch_get_global_queue(0, 0),^{ });//异步在新的线程 ...

随机推荐

  1. SQL pivot 基本用法 行列转换 数据透视

    SQL通过pivot进行行列转换 数据透视 可直接在sql server 运行 传统操作 和 pivot create table XKCl (name nchar(10) not null, 学科 ...

  2. 使用ajax获取JSON数据的jQuery代码的格式

    具体的也可以参考:http://www.w3cfuns.com/notes/16039/2b004b1bcdf79092f2e66b2bbe9f51df.html

  3. Codeforces Round #302 (Div. 2).C. Writing Code (dp)

    C. Writing Code time limit per test 3 seconds memory limit per test 256 megabytes input standard inp ...

  4. java实现文件单词频率统计 topN top K

    java 实现单词计数.top N 思路 先统计每个单词出现的个数 利用 TreeSet 的自动排序的功能 上代码 wordcount public void wordCount() { String ...

  5. MAVEN for mac 安装

    http://blog.csdn.net/anialy/article/details/22217937 下载 maven  http://mirrors.hust.edu.cn/apache/mav ...

  6. js兼容注意事项--仅供参考

    做BS开发就难免会用到javascript,而每个浏览器对javascript的支持有不同.这就需要我们程序员去兼容他们,不然有些浏览器就无法运行我们的代码.就会造来客户的投诉,如果让BoSS知道了, ...

  7. mongodb 维护

    如何释放空间? 1. 先用 remove 命令删除数据 2. repair.需要停机,即便你不停机的话 mongodb 自己也会锁住直到 repair 完成.注意要有足够的磁盘空间,需要额外一倍的空间 ...

  8. xcode免证书开发

    工程和target的code sign 选择 ios developer 工程的team选择为none就ok 推送测试 或者 正式发布的时候要改回来, 有一个严重的缺陷, 就是打包出来的ipa程序有时 ...

  9. 《C和指针(Pointer on c)》 学习笔记(转自:http://dsqiu.iteye.com/blog/1687944)

    首先本文是对参考中三个连接的博客进行的整理,非常感谢三位博主的努力,每次都感叹网友的力量实在太强大了…… 第一章 快速上手 1.  在C语言中用/*和*/来注释掉这段代码,这个实际上并不是十分的安全, ...

  10. [RouterOS] ROS对接碧海威或PA等流控实现完美流控详细教程(附脚本全免费)

    前言: 经常在群里看到不少朋友争论海蜘蛛 ROS 维盟 爱快 碧海威 流控大师 Woyos等等软路由,哪个好.实际上,网络产品是复杂的,现在的软路由功能上已经远远不是单独的路由了.每种产品都有他本身的 ...