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

由于想学习一下多线程编程,所以在百度搜索了一下,找到了一个外国网址- -那么就来翻译一下并且学习吧

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

为初学者准备的多线程和GCD教程

你是否曾经因为想要做什么事情而试着去编写一个app,而在UI没有回应的时候会有一个长时间的暂停.

这就是你的app需要多线程的标识.

在这个教程中,你将会着手于ios上的核心多线程API:GCD(多线程优化技术)

你可能会写一个没有使用多线程的应用(从此之后你的应用会十分迟钝…),一旦你通过使用多线程技术来将它改变.你会惊讶于巨大的差异.

        这个教程假定你对基础ios开发有一定的熟悉.如果你是一个ios开发小白,你应该在笨网站上查阅其他教程.

        言归正传,喝一口苏打或者嚼口香糖同时开始学习这个教程.—你已经踏上了多线程的路

 

我为什么要关心?

            “啊,你为什么要告诉我这些?我为什么要关心这个?我一点也不关心.你今天中午想要吃什么?”

            你就像一个木偶一样,你可能仍然会怀疑你为什么要关心这些多线程的问题.

         So,让我来给你展示一个没有使用多线程的例子.

           下载这个程序,用xcode打开编译,运行,你将会看到一个由vickiwenderlich.com返回的免费Game Artpack显示在屏幕上

测试的时候十分卡,刷了好长时间

 

这个app叫ImageGrabber,它的作用就是通过HTML访问这个网页,并且检索这些链接图片,然后将他们显示到tableview上以便你能更近的观察他们.

最炫酷的部分就是他能下载zip文件并且查看zip文件里的图片,就像这个网页上的free game art zip

继续然后点击”Grab”来观察他运行

…等待…

…等待…

…等待…

 

它成功了,但是时间太长了…这个app解析html,下载图片和zip文件,并且解压zip文件,全部是在主线程中进行的.

最后的结果就是用户需要呆坐在那里很长时间,并且不确定这个app是否在工作.

这样会导致很悲惨的后果:用户可能推出app,系统可能会因为app占用时间太长而终止app运行,或者你可能会得到一个生气的tomato攻击你的树屋.

        幸运的是,多线程来拯救你了,我们通过把这些在主线程中高强度的运算使用一些苹果提供的简单API来移动到后台.

 

多线程…和猫

如果你已经熟悉了多线程的概念,感觉这十分轻松想要跳到下一部分.事实上,你仍然是个新手—继续读下去

你认为一个程序在运行时,你可以把它看做一只用大箭头指向当前行的猫.猫随着程序的前进移动箭头.

        ImageGrabber app产生的问题是:我们通过把所有事情都在主线程中运行,导致我们的猫精疲力竭.所以在app重画UI或者响应事件之前,他需要花费所有时间来集中完成这些高强度工作,例如下载文件,解析html等等

        So我们怎么样才能让不堪重负的猫休息一会呢?解决方案很简单–买更多只猫(事实上,我有一个朋友非常擅长这个)

        那种方法,你主要的猫可以响应UI的刷新以及响应其他用户事件,同时你的剩余的猫去后台下载文件,解析html,跳到桌面上(结束)

        这就是多线程设计的要旨.就像这些猫东奔西跑执行任务,一个进程被分解成很多线程去执行.

        在IOS上,你需要声明的方法(例如viewDidLoad,buttontap callbacks)都运行在主线程中.你不会想在主线程中执行密集的工作任务,否则你将会得到一个没有响应的UI和一个快要累死的猫….

 

 

孩子们,不要在家做这些

        让我们来看一下代码,讨论一下它是怎么工作的-和它为什么差劲!

 

这个app的根视图控制器是WebViewController.当你点击(grab)键时,获取当前页面的HTML,并且将它传递给ImageListViewController

.       在ImageListViewController的viewDidLoad中,它创建了一个新的ImageManager 并且调用进程  .这个类连同ImageInfo一起,包含所有的耗时运行的代码,例如解析HTML,将图片从网上下载下来,解压文件

        让我们来看看这两个文件是怎么工作的:

ImageManager:processHTML:使用正则表达式在html中匹配搜索到得链接.这个可能潜在的耗费时间,取决于html有多大.对于它找到的每一个zip文件,都会调用retrieveZip.对于找到的图像文件,它会通过initWithSourceUrl初始化创建一个新的ImageInfo实例

ImageInfo:initWithSourceURL:通过使用异步方法[NsData dataWithContentsOfURL]调用getImage来取回网络上的图片.有很多例如[NsDatadataWithContentsOfURL]的方法,这些方法封锁执行时的流(the flow of execution)直到它完成,可能会花费很长时间,你几乎不会再在你的app中使用这些方法.

ImageInfo:retrieveZip:和以上所述的相似,使用可怕的[NSData dataWithContentsOfURL:...],这个东西会阻塞主线程直到它完成.当它完成的时候,它会调用processZip.

ImageInfo:processZip:使用ZipArchive库来把下载数据存储到硬盘中,并解压它,寻找里面的图片.写入硬盘和像这样解压是个很慢的操作,所以是另外一个不应该放到主线程中的实例.

 

你可能会注意到一些调用ImageManager的的方法-imageInfoAvailable.这是在当table中有新的入口(?)的时候ImageManager 通知table view的方法

观察一下并且确保你理解当前的执行流-(它为什么这么差劲).你也可能会觉得它运行时有用并且在它运行时观察控制台输出日志,你将会在代码运行时看到nolog的输出.

     如果你对它的当前工作有了良好的认识,让我们继续并且用多线程来提升它.

 

 

异步下载

           

            让我们来使用异步调用来代替这些超级慢的操作 – 下载文件.事实上,在使用苹果的类-NSURLRequest 和NSURLConnection,下载文件并不难.但是我是一个对于可以使这更简单的外包类-ASIHTTPRequest的粉丝.

            我们将要使用这个来异步下载文件,所以把它添加到你的工程中.

            如果你没有ASIHTTPRequest,先下载它.当你下载完毕后,在groups andfile中右击ImageGrabber工程入口,选择New Group,将新的group命名为ASIHTTPRequest,然后把ASIHTTPRequest\Classes directory(ASIAuthenticationDialog.h和其他的, 最重要的是不要把子文件夹移动例如 ASIWebPageRequest, CloudFiles, S3, and Tests.)拖拽到ASIHTTPRequest的group中.确保“Copy itemsinto destination group’s folder (if needed)”勾选.点击结束

         对于ASIHTTPRequest\External\Reachability这两个文件重复上述操作,因为这些是本工程的依赖.

         添加ASIHTTPRequest的最后一步是,你需要吧一些你需要的框架导入,点击ImageGrabber入口中得Groups &Files,点击PromoTest target,选择Build Phasestab 和expand the Link Binary with Libraries部分.点击添加按钮,选择CFNetwork.framework.然后添加SystemConfiguration.framework和MobileCoreServices.framework.

         现在是时候来用异步的优秀代码来代替差劲的同步代码!

         打开ImageManager.m做出如下改变

#import "ASIHTTPRequest.h"
 
// Replace retrieveZip with the following
- (void)retrieveZip:(NSURL *)sourceURL {
    NSLog(@"Getting %@...", sourceURL);
    __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:sourceURL];
    [request setCompletionBlock:^{
        NSLog(@"Zip file downloaded.");
        NSData *data = [request responseData];
        [self processZip:data sourceURL:sourceURL];        
    }];
    [request setFailedBlock:^{
        NSError *error = [request error];
        NSLog(@"Error downloading zip file: %@", error.localizedDescription);
    }];
    [request startAsynchronous];    
}



这里通过一个给定的URL来创建ASIHTTPRequest,它创建了一个当请求结束或者因某些原因请求失败,运行的方法的代码块.

然后调用startAsynchronous.这个方法可以迅速返回,所以主线程可以在UI变化和相应用户输入持续运行.同时,系统在后台线程会自动运行代码下载zip文件,当它完成或者失败的时候会调用代码块.

            相似的改变ImageInfo.m

// Add to top of file
#import "ASIHTTPRequest.h"
// Replace getImage with the following
- (void)getImage {
    NSLog(@"Getting %@...", sourceURL);
    __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:sourceURL];
    [request setCompletionBlock:^{
        NSLog(@"Image downloaded.");
        NSData *data = [request responseData];
        image = [[UIImage alloc] initWithData:data];
    }];
    [request setFailedBlock:^{
        NSError *error = [request error];
        NSLog(@"Error downloading image: %@", error.localizedDescription);
    }];
    [request startAsynchronous];    
}



这和其他代码更相似-它在后台运行下载,当完成的时候改变image变量

让它运行一下!编译运行然后点击”Grab” –他可以快速地生成详细tab而不是经历一个长时间的暂停!但是,仍然有一个主要的问题:



图片在它们下载完毕后并没有在tableview中显示!你可以通过向上或者想下滑动table来显示他们.但是这是一种bug(?),我们怎么才能修复它?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值