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)控制。

如我们一项任务需要涉及数据下载,数据存储,视图渲染和界面展现几个流程,我们可以分别创建downloadQueuestoreQueue, 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异步编程的更多相关文章

  1. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  2. 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 ...

  3. 关于如何提高Web服务端并发效率的异步编程技术

    最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...

  4. 异步编程 In .NET

    概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...

  5. C#异步编程(二)

    async和await结构 序 前篇博客异步编程系列(一) 已经介绍了何谓异步编程,这篇主要介绍怎么实现异步编程,主要通过C#5.0引入的async/await来实现. BeginInvoke和End ...

  6. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  7. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  8. [C#] 走进异步编程的世界 - 开始接触 async/await

    走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...

  9. C#异步编程

    什么是异步编程 什么是异步编程呢?举个简单的例子: using System.Net.Http; using System.Threading.Tasks; using static System.C ...

  10. 深入解析js异步编程利器Generator

    我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ...

随机推荐

  1. cojs 奈特 题解报告

    才知道knight念奈特,而不念科耐特 这个题显然是一个数据结构题目,我搬运的CF上的题 CF的题解好长超长哒,而且可以在线,但是并不能看懂 于是自己想了一个一种做法A掉了,唯一的缺陷就是做法有些繁琐 ...

  2. cv 论文(CNN相关)

    最近发现很多以前看的论文都忘了,所以想写点东西来整理下之前的paper,paper主要是cv(computer vision)方向的. 第一篇:Gradient-based learning appl ...

  3. 毕向东JAVA视频视频讲解(第八课)

    继承的好处: 1,提高了代码的复用性. 2,让类与类之间产生了关系,给第三个特征多态提供了前提. java中支持单继承.不直接支持多继承,但对C++中的多继承机制进行改良. 单继承:一个子类只能有一个 ...

  4. linux进程的地址空间,核心栈,用户栈,内核线程

    linux进程的地址空间,核心栈,用户栈,内核线程 地址空间: 32位linux系统上,进程的地址空间为4G,包括1G的内核地址空间,和3G的用户地址空间. 内核栈: 进程控制块task_struct ...

  5. Swift 版本很好的卡片切换效果基于ZLSwipeableView

    前言:在这篇文章你可以学到,一些基本的Swift语法, 基本UI控件闭包等. 实际的效果,比gif图的效果好很多. 卡片切换.gif 首先需要导入ZLSwipeableView pod 'ZLSwip ...

  6. ios UIView常用动画效果

    一 //调用 1 2 3 4 5 6 if(m_viewScenario.superview == nil)<br>{     m_viewScenario.alpha = 1.0;    ...

  7. Generic repository pattern and Unit of work with Entity framework

    原文 Generic repository pattern and Unit of work with Entity framework Repository pattern is an abstra ...

  8. java中文排序

    对中文名称进行排序,不多说,上代码 package test; /** * @Title: Person.java * @Copyright: Copyright (c) 2012-11-19 * @ ...

  9. Javascript如何判断一个变量是数字类型?

    isNaN()不能判断一个变量是否为数字类型,isNaN(123)值为false,isNaN('123')值也为false.isNaN() 的实际作用跟它的名字isNaN并不一致,isNaN(NaN) ...

  10. Ubuntu 安装mod_python配置Apache2

    在Ubuntu上搭建Python运行环境,mod_python是不可少的(据说mod_swgi也是可以的,没有亲测).使用命令安装mod_python. 安装: apt-get install lib ...