Objective-C异步编程
1. 不要阻塞主线程
不管在进行iOS还是OS X开发中,主线程都只应该处理用户交互和界面布局,好的程序通常能够随时快速响应用户的操作,所以CPU密集型或者会阻塞线程的代码应该在其他位置去执行,我指的是其他线程。
2. 在后台线程中执行
为了不阻塞主线程,我们应该把更多的操作放到后台中去执行,只有在不得不在主线程中执行时(更新UI等)才回到主线程,GCD是最适合这种线程之间切换的:
//Main Thread
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self renderThumbnails];
dispatch_async(dispatch_get_main_queue(), ^{
[self.thumbnailView setNeedsDisplay:YES];
});
});
3. 不要阻塞太多后台线程
如果我们要在后台线程中请求一系列的数据,然后将它们显示到界面上,你可能写出下面的代码:
//Main Thread
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSURL *url in [self.imageStore URLs]) {
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url]; dispatch_async(dispatch_get_main_queue(), ^{
[self.imageStore setImageData:data forURL:url];
});
});
}
这段代码肯定是有问题的,因为获取数据NSData *data = [NSData dataWithContentsOfURL:url];是同步的,台线程被这段代码阻塞调,系统会自动创建新的线程去执行下一个循环,最终结果会是获取多少次数据将创建了多少个后台线程。而创建线程本身是有成本的,所以如果创建太多的后台线程会占用大量的系统资源,这时应该用dispatch I/O来解决:
//Main Thread
for (NSURL *url in [self.imageStore URLs]) {
dispatch_io_t io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, [[url path] fileSystemRepresentation], 0_RDONLY, 0, NULL, NULL);
dispatch_io_set_low_water(io, SIZE_MAX); dispatch_io_read(io, 0, SIZE_MAX, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
[self.imageStore setImageData:data forURL:url];
});
}
4. 与主循环(Main Runloop)结合
通常我们一系列后台执行代码结束后,需要将结果反馈到主线程中,我们可以直接调用 dispatch_get_main_queue() 获取主线程,并在其中执行代码。
还有一些API是带有基于runloop的回调的,如NSTimer、一些performSeletor:方法和代理方法回调,所有这些API都会默认回调函数所在的runloop,所以在使用这些API时应该知道回调方法的runloop是属于哪个线程。还应该注意两点:
- 不要在自动分配的工作线程中调用这些API
- 不要阻塞在main runloop中的回调函数
- (void)downloadFromRemotePictureViewer:(NSString *)name {
//Main Thread
NSNetService *service = [[NSNetService alloc] initWithDomain:@"" type:@"_pictureviewer._tcp" name:name];
[service setDelegate:self];
[service resolveWithTimeout:5.0];
}
- (void)netServiceDidResolveAddress:(NSNetService *)service {
[self downloadFromRemoteService:service];
}
在上面初始化和发起NetService请求都应该在主线程执行,如果你通过GCD让它在后台运行,那么它的代码回调函数是永远也不会被调用,与此类似的还有NSURLConnection。代理方法也默认是在主线程中调用的,所以为了不阻主线程,我们应该将回调里面的处理放在后台:
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(self.downloadQueue, ^{
[self downloadFromRemoteService:service];
});
}
5. 为每个子系统对应一个队列
通常我们应该将程序分割成多个独立的子系统,通过对应的调度队列来控制每个部分,界面部分由主队列(Main Queue)控制。
如我们一项任务需要涉及数据下载,数据存储,视图渲染和界面展现几个流程,我们可以分别创建downloadQueue,storeQueue, renderQueue,界面展现则只需要使用“main queue”。
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(self.downloadQueue, ^{
NSData *data = [self downloadFromRemoteService:service];
dispatch_async(self.storeQueue, ^{
int img = [self.imageStore addImage:data];
dispatch_saync(self.renderQueue, ^{
[self renderThumbnail:img];
dispatch_async(dispatch_get_main_queue(), ^{
[[self thumbnailViewForId:img] setNeedsDisplay:YES];
});
});
});
});
}
6. 通过读写访问提升效率
我们在设计读写时通常允许并发同步的的读(read),串行异步的写(write),并且读写不能同时进行。
self.concurrentQuene = dispatch_queue_create("com.example.current", DISPATCH_QUEUE_CONCURRENT);
- (id)objectAtIndex:(NSUInteger)index {
__block id obj;
dispatch_sync(self.concurrentQueue, ^{
obj = [self.array objectAtIndex:index];
});
return obj;
}
- (void)insertObject:(id)obj atIndex:(NSUInteger)index {
dispatch_barrier_async(self.concurrentQueue, ^{
[self.array insertObject:obj atIndex:index];
});
}
7. 区分控制和数据流
调度队列(dispatch queue)并不是为一般的数据存储而设计的,它没有取消操作和随机存储,所以需要合理使用数据结构。
假设我们有一组图片需要渲染,如果我们每渲染一张图片时都去存储队列中读取对应的数据,那个渲染队列和存储队列就会因为依赖的大大降低执行效率。我们可以合理的利用数据结构,如我们可以每次从存储队列中取多个图片然后渲染,完后再去存储队列中取,这样就大大减少了依赖,而且也避免了频繁的队列切换。
8. 异步的更新状态
有时候我们先知道队列中操作执行的进度,并通过状态显示出来,如通过progress view显示当前图片渲染的进度,我们可以使用GCD的dispatch source。
//先设置接受到数据的处理(类似监听)
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_ADD, 0, 0, dispatch_get_main_queue()); dispatch_source_set_event_handler(self.source, ^{
self.progress += dispatch_source_get_data(self.source);
[self.progressView setProgress:(self.progress/self.total) animated:YES];
});
dispatch_resume(self.source);
//在渲染的时候将数据传递给dispatch source
dispatch_async(self.renderQueue, ^{
//...
dispatch_source_merge_data(self.source, 1);
});
//可以取消掉dispatch source的处理
dispatch_source_cancel(self.source);
Posted by TracyYih - Aug 28 2013
如需转载,请注明: 本文来自 Esoft Mobile
Objective-C异步编程的更多相关文章
- C#异步编程(一)
异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...
- C#与C++的发展历程第三 - C#5.0异步编程巅峰
系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...
- 关于如何提高Web服务端并发效率的异步编程技术
最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...
- 异步编程 In .NET
概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...
- C#异步编程(二)
async和await结构 序 前篇博客异步编程系列(一) 已经介绍了何谓异步编程,这篇主要介绍怎么实现异步编程,主要通过C#5.0引入的async/await来实现. BeginInvoke和End ...
- [.NET] 利用 async & await 的异步编程
利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html 目录 异步编程的简介 异 ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- [C#] 走进异步编程的世界 - 开始接触 async/await
走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...
- C#异步编程
什么是异步编程 什么是异步编程呢?举个简单的例子: using System.Net.Http; using System.Threading.Tasks; using static System.C ...
- 深入解析js异步编程利器Generator
我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ...
随机推荐
- Android中使用HTTP和HttpClient进行通信
/** * 使用HTTP的Get方式进行数据请求 */ protected void httpGet() { /** * 进行异步请求 */ new AsyncTask<String, Void ...
- lintcode 中等题:N Queens II N皇后问题 II
题目: N皇后问题 II 根据n皇后问题,现在返回n皇后不同的解决方案的数量而不是具体的放置布局. 样例 比如n=4,存在2种解决方案 解题: 和上一题差不多,这里只是求数量,这个题目定义全局变量,递 ...
- 使用PowerDesigner进行数据库建模入门
阅读目录 两种重要模型 创建表和主外键 创建视图和存储过程 生成数据库 PowerDesigner(简称PD)是一种强大的数据库建模工具,使用PD可以创建业务模型,UML类图等,当然最主要的功能是数据 ...
- js错误:对象不支持此属性或方法
对象不支持此属性或方法 错误原因: 可能是js的文件名和另外一个文件重复. 也有可能是js里的function和另外一个function名字重复. 也有可能是js里的function和页面的某一元素重 ...
- 奇怪的transform bug
对一个元素使用transform:rotate 进行旋转,造成: 父元素的背景图位置偏移,往下降,背景图也会变模糊一些 造成重绘,导致该元素后面的兄弟元素受到影响,变得模糊,并且无法遮盖住父元素的背景 ...
- 如何创建支持Eclipse IDE的Maven项目
使用Maven创建的项目是不支持任何IDE的,不能导入IDE中,因为项目格式都不符合特定IDE的格式要求,那么如何创建符合IDE要求的项目呢? 1.使用mvn eclipse:eclipse 命令把项 ...
- Android 下log的使用总结
Android 下log的使用总结 一:在源码开发模式下 1:包含头文件: #include <cutils/log.h> 2:定义宏LOG_TAG #define LOG_TAG &qu ...
- Atheros AR9485 ubuntu 10.04 驱动安装及networking disable问题解决
Laptop: ACER Aspire 5733-6629 Wireless:Lite-on HB125, CHIPS: Atheros AR9485 Ubuntu: 10.04LTS (2.6.32 ...
- Android Activity形象描述
Activity就是形象的说就是一个容器,在里面放置各种控件(按钮,文本,复选框等),就形成了软件的界面~ Activity是可见的,如果不加任何控件的话,那么就像Windows中的空白窗体一样 通过 ...
- poj 2528 Mayor's posters(线段树)
题目:http://poj.org/problem?id=2528 题意:有一面墙,被等分为1QW份,一份的宽度为一个单位宽度.现在往墙上贴N张海报,每张海报的宽度是任意的, 但是必定是单位宽度的整数 ...