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

最新文章

  1. C#-WinForm-Winform TextBox中只能输入数字的几种常用方法(C#)
  2. 实用的Portraiture滤镜磨皮教程
  3. 去除IE6浏览器下获得焦点的元素的虚线框的两个小办法
  4. 【读书笔记】iOS-安全地传输用户密码的方法
  5. ASP.NET MVC3 Model验证总结(转)
  6. Java根据html模板创建 html文件
  7. 使用GSoap开发WebService客户端与服务端
  8. hadoop namenode多次格式化后,导致datanode启动不了
  9. Eclipse 支持jQuery 自动提示
  10. Qt Creator 代码自动补全设置
  11. 573. Squirrel Simulation
  12. You may rarely look at it. But you'll always feel it
  13. UnityShader-菲涅尔反射(Fresnel Reflection)
  14. DEDECMS中的几个常见的自定义常量DEDEMEMBER等位置
  15. Solr搜索引擎搭建详细过程
  16. 执行docker命令遇到 Get Permission Denied
  17. SeaJS之use方法
  18. 西部数码虚拟空间配置ssl
  19. centos 7.4安装zabbix 3
  20. Android 4.4 根据uri获取路径的方法

热门文章

  1. 通过一个简单的数据库操作类了解PHP链式操作的实现
  2. 【Linux高频命令专题(10)】mv
  3. 【Linux高频命令专题(9)】ls
  4. 252. Meeting Rooms
  5. NSArray 数组排序
  6. js中鼠标滚轮事件详解
  7. Map集合案例
  8. Entity Framework学习 - 1.连接数据库
  9. POJ 3185 The Water Bowls(高斯消元-枚举变元个数)
  10. python 字符串换行的三种方式