关于多线程和GCD新手教程(二)

原文链接:http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial

接上文:关于多线程和GCD新手教程(一)

一个简单的方式就是从你的代码中的一部分刷新另外一部分代码,这是苹果内置的NSNotification消息系统.它的确很简单.你可以通过[NSNotificationCenter defaultCenter]取得NSNotificationCenter(消息中心)的单例而且:

1.如果你有想要刷新UI或代码的地方,你应该调用postNotificationName方法.你仅仅需要提供一个独特的字符串(例如:com.razeware.imagegrabber.imageupdated)以及一个对象(例如你刚完成下载的图片类ImageInfo)

2.如果你想找出这个更新什么时候发生,你可以调用addObserver:selector:name:object方法.在我们的样例中 ImageListViewController 会注意更新什么时候发生,所以它可以适当的加载tableview的cell.放置该函数最好的地方就是在viewDidLoad:中

3.当视图将要销毁不要忘记调用removeObserver:name:object .否则消息系统可能会在你销毁的视图(或者更坏的情况是未分配内存的对象)调用其他方法,这种事情的后果很严重.

所以我们来试着解决它.打开ImageInfo.m 并且做出如下修改

// Add inside getImage, right after image = [[UIImage alloc] initWithData:data];
[[NSNotificationCenter defaultCenter] postNotificationName:@"com.razeware.imagegrabber.imageupdated" object:self];

图片一旦被下载,我们就会发送一个通知并且将这个刚更新的对象传过去.

下一步转到ImageListViewController.m 并且做出如下修改:

// At end of viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageUpdated:) name:@"com.razeware.imagegrabber.imageupdated" object:nil];

// At end of viewDidUnload
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.razeware.imagegrabber.imageupdated" object:nil];

// Add new method
- (void)imageUpdated:(NSNotification *)notif {

    ImageInfo * info = [notif object];
    int row = [imageInfos indexOfObject:info];
    NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:0];

    NSLog(@"Image for row %d updated!", row);

    [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];

}

在viewDidUnload方法中关于通知的注册器,简单来说就是:”当消息到来的时候请调用imageUpdate”.同样我们也可以在viewDidUnload方法中适当的取消注册器.

调用imageUpdate本质上就是对象中传递的Imageinfo的数组.一旦找到了它,我们就能得到它是哪一行并且告诉tableview来更新那一行.

编译运行之后你就会看到图片在他们下载完毕之后会跳出.

GCD和调度队列,我的天!

我们的应用中仍然会有一个问题.如果你点击了”Grab!”并且在detail视图加载的时候持续的上下滑动,在zip文件下载的时候你会看到全部的UI忧郁存储和解压zip文件而被冻住.

这是由于在ASIHTTPRequest中的完成块在主线程中被调用,我们调用这段代码来在主线程中解决这些问题:

[request setCompletionBlock:^{

NSLog(@"Zip file downloaded.");

NSData *data = [request responseData];

[self processZip:data sourceURL:sourceURL]; // Ack - heavy work on main thread!

}];

我们如何才能让这种高负荷的工作运行在后台呢?

在ios3.2介绍了一个非常简单(也非常有效)的方式通过GCD系统来解决这个问题.从根本上来说,无论何时当你想在后台运行一些东西的时候,你可以调用dispatch_async并且写入一些你想运行的代码.

GCD将会为你解决所有的细节-它会在你需要的时候为你创建一个新的线程,或者重用一个以前的可用线程.

当你调用dispatch_async,你就会进入一个调度队列(dispatch_queue).你可以把这个想成一个先进先出列表来存储所有你写入的代码块.

你可以创建你的调度队列(通过dispatch_create).或者获得一个特别的主线程队列(通过dispatch_get_main_queue).我们可以创建一个称谓”backgroundQueue”的后台队列,用之来在后台运行进程任务,像解析XML或者存储/解压zip文件.

线程队列,加锁,以及猫粮

当一个调度队列被创建时它默认是连续的-这意味着在同一时间只有只有一块代码可以在队列中运行.这种方式确实会很便捷,因为你可以用这种方式来保护你的数据.

如果你不是非常熟悉多线程加锁,想想我们之前猫的例子.如果两只猫同时都想到猫粮盒中吃猫粮会发生什么问题?这就是那个大问题!

但是如果我们使所有的猫都排成一列.然后我们说:”喵咪们,如果你们想接近这个盘子,你们必须站在这条线里!”只要是这样事情就简单多了!

那就是使用调度队列来保护数据的基本想法.当你编写代码以便使一个特殊的数据结构只能被在调度队列中运行的代码使用.然后随着调度队列连续运行,你会保证只有一个代码块会在同一时间中访问这个数据结构.

在这个应用中我们需要保护两个数据结构:

1.在ImageListViewController中的linkUrls数组 .为了保护它,我们编写代码来使它只能在主线程中运行.

2.在ImageManager中的pendingZips变量.为了保护它,我们编写代码让它只能在后台队列中运行.

关于GCD我们聊的已经足够了-让我们来试试它!

GCD练习

让我们从打开ImageGrabber.h开始并且做出如下改变:

// Add to top of file
#import <dispatch/dispatch.h>

// Add new instance variable
dispatch_queue_t backgroundQueue;

为了使用GCD,首先需要导入.我们也需要预先编译调度队列来运行我们的后台进程任务.

下一步打开ImageGrabber.m并且做出如下变化:

// 1) Add to bottom of initWithHTML:delegate
backgroundQueue = dispatch_queue_create("com.razeware.imagegrabber.bgqueue", NULL);        

// 2) Add to top of dealloc
dispatch_release(backgroundQueue);

// 3) Modify process to be the following
- (void)process {
    dispatch_async(backgroundQueue, ^(void) {
        [self processHtml];
    });
}

// 4) Modify call to processZip inside retrieveZip to be the following
dispatch_async(backgroundQueue, ^(void) {
    [self processZip:data sourceURL:sourceURL];
});

// 5) Modify call to delegate at the end of processHTML **AND** processZip to be the following
dispatch_async(dispatch_get_main_queue(), ^(void) {
    [delegate imageInfosAvailable:imageInfos done:(pendingZips==0)];
});

这些都是很简单但是却很重要的调用,让我们来挨个讨论一下他们.

1.这条创建了一个调度队列.当你创建了一个调度队列时需要给他一个独特的名字(字符形式)

2.当你创建了一个调度队列,不要忘记释放释放它!对于这个队列,我们需要在ImageManager销毁的时候释放它

3.旧的进程会立即运行processHTML,因此在主线程中运行解析HTML的时候它会阻塞UI刷新.现在我们将它运行在我们创建的后台队列中,只要简单的调用dispatch_async!

4.相似的,在我们下载zip之后我们会在主线程中得到一个ASIHTTIRequest的回调:”hey,我做完了”.作为我们之前存储以及解压zip文件阻塞UI刷新的替代,我们现在将它运行在后台队列.这对于保证pendingZips变量也很重要.

5.我们想要确保我们在主线程上下文中调用代理方法.首先根据我们之前的策略,为了保证在viewcontroller中的linkURLs数组只能通过主线程访问.其次这个方法会和UIKit对象相互作用,UIKit对象只能被主线程使用.

那正是要点!编译并且运行你的代码.imageGrabber应该表现的更出色.

http://cdn1.raywenderlich.com/wp-content/uploads/2011/07/ResponsiveApp.jpg

但是等一下!

如果你只是暂时的开发ios,你可能会听到这些被称为NSOperations以及操作队列的想法.你可能会疑惑什么时候使用它们,什么时候该使用GCD.

NSOperations只是一个基于GCD的简单的API.所以当你在使用NSOperation时,其实你仍旧在使用GCD.

NSOperations会给你一些你喜欢的特点.你可以创建一些基于其他操作的操作,在你提交队列之后重新排列你的队列以及类似的特点.

事实上,ImageGrabber 已经使用了NSOperations和操作队列!ASIHTTPRequest在底层使用它们,你可以把操作队列配置成你想要的那样.

所以你该使用哪个?取决于你的应用,关于这个应用,他比较简单所以我们只需要使用GCD,不需要其他的NSOperation的特点.但是如果你在应用中需要这些特点,快点使用它吧!

时间: 2024-10-14 04:27:37

关于多线程和GCD新手教程(二)的相关文章

C#微信公众号开发系列教程二(新手接入指南)

http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可直接跳过,也欢迎大神吐槽. 目录 C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) 微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流程是这样的,用户发送消息到微信服务器,微

OSG for Android新手教程系列(二)——项目配置

在上一篇教程中,主要介绍了如何把OSG源代码编译成为能够在Android项目下使用的函数库.在这一篇教程中,我将针对如何在自己的Android项目中配置OSG函数库进行详细讲解. 现阶段网上关于OSGfor Android的配置方式教程有很多,但是大部分在实际使用起来都会或多或少的出现一些问题,无法完全照搬,需要一定的修改.而且,对于配置中的那些变量的具体含义,也很少有人能够进行仔细的讲解.这非常不利于新手的学习和理解,往往会造成出现bug后面对满屏幕的错误log完全一脸茫然的情况. 所以我将在

有用PHP依赖管理工具Composer新手教程

PHP依赖管理工具Composer新手教程 Composer 是 PHP 的一个依赖管理工具.它同意你申明项目所依赖的代码库,它会在你的项目中为你安装他们. 依赖管理 Composer 不是一个包管理器. 是的,它涉及 "packages" 和 "libraries",但它在每一个项目的基础上进行管理,在你项目的某个文件夹中(比如 vendor)进行安装. 默认情况下它不会在全局安装不论什么东西.因此,这不过一个依赖管理. 这样的想法并不新奇,Composer 受到

Apple Swift编程语言新手教程

Apple Swift编程语言新手教程 作者: 日期: gashero 2014-06-03 FROM:http://gashero.iteye.com/blog/2075324 文件夹 1   简单介绍 2   Swift入门 3   简单值 4   控制流 5   函数与闭包 6   对象与类 7   枚举与结构 1   简单介绍 今天凌晨Apple刚刚公布了Swift编程语言,本文从其公布的书籍<The Swift Programming Language>中摘录和提取而成.希望对各位的

【OpenCV十六新手教程】OpenCV角检测Harris角点检测

本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/29356187 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本号: 2.4.9 本篇文章中,我们一起探讨了OpenCV

MATLAB新手教程

MATLAB新手教程   1.MATLAB的基本知识 1-1.基本运算与函数    在MATLAB下进行基本数学运算,仅仅需将运算式直接打入提示号(>>)之後,并按入Enter键就可以.比如: >> (5*2+1.3-0.8)*10/25 ans =4.2000 MATLAB会将运算结果直接存入一变数ans,代表MATLAB运算後的答案(Answer)并显示其数值於萤幕上. 小提示: ">>"是MATLAB的提示符号(Prompt),但在PC中文视窗

【OpenCV新手教程之十八】OpenCV仿射变换 &amp;amp; SURF特征点描写叙述合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本号: 2.4.9 本篇文章中.我们一起探讨了OpenCV

iOS开发多线程篇—GCD的基本使用

iOS开发多线程篇—GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程. 获取主队列的方式: dispatch_queue_t queue=dispatch_get_main_queue(); (1)使用异步函数执行主队列中得任务,代码示例: 1 // 2 // YYViewController.m 3 //

C语言快速入门教程(二)

C语言快速入门教程(二) C语言的基本语法 本节学习路线图: 引言: C语言,顾名思义就是一门语言,可以类比一下英语; 你要说出一个英语的句子需要:  单词  +  语法!  将单词按照一定的语法拼凑起来就成了一个英语句子了; C语言同样是这样,只不过单词可以理解为一些固定的知识点,而语法可以理解为算法(可以理解为解决问题的方法) 在这一节中我们就对固定知识点中的语言描述与数据存储进行解析! 1.C语言的基本元素 1.1  标识符 什么是标识符? 答:在C语言中,符号常量,变量,数组,函数等都需