GCD

使用GCD

什么是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
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
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个方法都可以放到一起,如下所示:

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 
// 原代码块一
self.indicator.hidden = NO; [self.indicator startAnimating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  // 原代码块二  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的定义有点象函数指针,差别是用 ^ 替代了函数指针的 * 号,如下所示:

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

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

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

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

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

系统提供的dispatch方法

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

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 
 //  后台执行:
 dispatch_async(dispatch_get_global_queue(0, 0), ^{  // 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方法,示例如下:

1
2
3
4
5
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来实现,示例如下:

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

修改block之外的变量

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

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

后台运行

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

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

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 
// 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能够极大地方便开发者进行多线程编程。大家应该尽量使用GCD来处理后台线程和UI线程的交互。

转载于:https://www.cnblogs.com/wq-gril/p/4725647.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试技术——系统测试

文章目录一、功能测试二、回归测试定义测试时机三、性能测试定义目标性能测试类型压力负载测试1.并发性能测试(重点)2. 疲劳强度测试3. 大数据量测试压力测试&负载测试四、其他非功能测试1. 安全性测试功能性测试&安全性测试2. 可靠性3. 容错性测…

nagios-3.4.3搭建

nagios转载于:https://blog.51cto.com/yujianglei/1557718

.NET Core程序瘦身器发布,压缩程序尺寸到1/3

.NET Core具有【剪裁未使用的代码】的功能,但是由于它是使用静态分析来实现的,因此它的剪裁效果并不是最优的。它有如下两个缺点:不支持Windows Forms和WPF,而对于程序剪裁功能需求最强烈的其实反而是桌面程序的开发者。无法删除运…

Android之切换账号登录依然能登录成功问题解决办法

1、问题 切换账号登录依然能登录成功 2、原因和解决办法 原因是因为我调用了第三方的sdk,里面有个生成签名文件的函数,写死了一个参数,导致每次生成签名文件内容都是一致,导致到服务端验证通过成功。 分析路线: 1、 先打印日志…

《大型网站服务器容量规划》一1.1 容量规划背景

本节书摘来异步社区《大型网站服务器容量规划》一书中的第1章,第1.1节,作者: 郑钢 责编: 张涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.1 容量规划背景 如今人们已经习惯从互联网上获取信息&#xf…

iptables配置详解

-A参数是将规则写到现有链规则的最后面-I 参数默认是将一条规则添加到现有规则链的最前面,当然也可以指定插入到第几行 行数可以用数字来指定 比如说将一条规则添加到某一条链的第三行 那么原来在第三行的规则就会降到下一行第四行。例如: iptables -I …

软件测试技术——单元测试和集成测试

一、单元测试 为何要进行单元测试? 尽早发现错误 错误发现越早,成本越低。发现问题比较容易修正问题更容易 1.定义 单元测试是对软件基本的组成单元进行独立的测试 2.目标 单元模块是否被正确编码。信息能否正确地流入和流出单元。在单元工作过程中…

文本框输入值文字消失常用的两种方法

1.这种相对来说较简单&#xff0c;举例子&#xff1a; <input name"textfield" type"text" value"点击添入标题" style"color: gray;" onfocus"if (value 点击添入标题){value }" onblur"if (value ){value点击添…

C语言之strstr函数类似Java字符串的contain函数

1、strstr函数介绍 找出haystack字符串在needle字符串中第一次出现的位置&#xff08;不包括needle的串结束符&#xff09;。返回该位置的指针&#xff0c;如找不到&#xff0c;返回空指针。 2、举例 3、运行结果

软件测试技术——软件测试概述

文章目录一、软件测试的定义标准定义Bug和缺陷二、软件测试与软件质量保证三、软件测试七大基本原则四、软件测试分类按测试手段按测试执行方式按测试阶段或层次按测试对象五、软件测试过程模型V模型W模型H模型X模型一、软件测试的定义 正向观点逆向观点验证软件是否能正常工作…

《编写高质量代码:改善c程序代码的125个建议》——建议14-2:在右移中合理地选择0或符号位来填充空出的位...

本节书摘来自华章计算机《编写高质量代码&#xff1a;改善c程序代码的125个建议》一书中的第2章&#xff0c;建议14-2,作者&#xff1a;马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。 建议14-2&#xff1a;在右移中合理地选择0或符号位来填充空出的位 在右移…

Linux-No.04 Linux 设置定时任务发送邮件功能

2019独角兽企业重金招聘Python工程师标准>>> 1、定时任务 crontab -l crontab -e /sbin/service crond start //启动服务/sbin/service crond stop //关闭服务 /sbin/service crond restart //重启服务 /sbin/service crond reload //重新载入配置 /sbin/…

WPF 基础控件之 DataGrid 样式

此群已满340500857 &#xff0c;请加新群458041663由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。支持NugetInstall-Package WPFDevelopers.Minimal -Version 3.2.001—代码如下一、创建…

C#页面抓取信息

//根据Url地址得到网页的html源码private string GetWebContent(string Url){string strResult"";try{HttpWebRequest request (HttpWebRequest)WebRequest.Create(Url);//声明一个HttpWebRequest请求request.Timeout 30000;//设置连接超时时间request.Headers.Set…

C语言之unsigned char和uint8_t

1、问题 今天搞openssl里面的客户端和服务端生成的随机数时候&#xff0c;PC端用的uint8_t数据类型&#xff0c;我用的是unsigned char类型&#xff0c;特么这不日了狗&#xff0c;不一样&#xff0c;后来问了pc端的人&#xff0c;和到网上查了下他们的区别 2、解释 unsigned…

电子商务应用课程知识整理 第五章-搜索引擎优化(SEO)

文章目录一、认识SEOSEO概述SEO相关术语SEO黑帽SEO白帽PV&#xff08;Page View&#xff09;跳出率沙盒效应SPAM快照二、SEO常用技术1. 网页title的优化要点&#xff1a;2. 关键词的选取注意的问题&#xff1a;需要避免的问题&#xff1a;3. 元标签关键词的优化要点&#xff1a…

在 Linux 下使用 RAID(二):使用 mdadm 工具创建软件 RAID 0 (条带化)

RAID 即廉价磁盘冗余阵列&#xff0c;其高可用性和可靠性适用于大规模环境中&#xff0c;相比正常使用&#xff0c;数据更需要被保护。RAID 是一些磁盘的集合&#xff0c;是包含一个阵列的逻辑卷。驱动器可以组合起来成为一个阵列或称为&#xff08;组的&#xff09;集合。 创建…

【Maven实用技巧】03. Maven 编译打包时如何忽略测试用例

跳过测试阶段&#xff1a; mvn package -DskipTests 临时性跳过测试代码的编译&#xff1a; mvn package -Dmaven.test.skiptrue maven.test.skip同时控制maven-compiler-plugin和maven-surefire-plugin两个插件的行为&#xff0c;即跳过编译&#xff0c;又跳过测试。 指定测试…

如何使用windbg查看C#某个线程的栈大小 ?

每一个线程都有一个叫 TEB&#xff08;Thread Environment Block&#xff09; 的线程环境块数据结构&#xff0c;这个结构中有一个叫做 NT_TIB 的结构&#xff0c;它里面有两个字段分别为 StackBase 和 StackLimit&#xff0c;前面叫做栈基址,也就是栈顶&#xff0c;后者叫做 栈…

Android之解决ubuntu没有无线网卡和手机wifi实现adb wifi调试

1、问题 ubuntu没有无线网卡&#xff0c;但是在在wifi1是同一个网段&#xff0c;然后手机需要wifi adb调试 2、解决办法 1、连接上wifi1 2、手机下载“终端模拟器apk”安装在手机上 3、手机连接电脑&#xff0c;然后adb tcpip 5555 4、然后adb push dropbearmulti /sdcard…