mlyixi's Blog

欲望的唤醒,信仰的坚定
ios without interface builder 开发
ios内存管理总结 (非ARC)

多线程和GCD初学者教程

mlyixi posted @ 2012年8月01日 22:34 in 未分类 , 2530 阅读

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

当不使用多线程时,UI(主线程)经常变得不可响应. 该教程针对有相关开发经验者介绍多线程,如果是新手,请先查看更多教程.

一.为什么关注?

首先通过一个没有用多线程的实际的例子进行说明.

下载该工程,用xcode打开并运行,你将在屏幕上看到一个免费的游戏包(网页).

这个app的任务是把该网页中的所有图片都下载下来,并把它们在一个tableview中显示出来.

运行后你会发现该app经常无法响应用户操作,如grap按钮.

因为该app解析html,下载图片和zip文件,解压等操作都在主线程中进行.

二.多线程...和猫!(作者爱猫?)

如果你已经熟悉多线程的概念,那么就跳到下一节吧,否则,请继续.

当你想像程序的运行时,你可以认为是一只带着箭头的猫,这箭头指向某一行代码....(汗). 这只猫移动这个箭头使得程序运行.

app的关键问题是这只可怜的猫已经精疲力尽了,因为它又要管UI(响应用户事件)和大量数据处理.

多可怜啊,它需要休息.

解决文案很简单:再买几只猫.(买猫怕养不活哦).

现在,你的第一只猫负责更新UI和响应用户事件,而其它猫负责暗地里的下载文件,解析html等等等等.

这个就是多线程的要旨.

在ios中,你经常要实现的方法如viewDidLoad,button tap callbacks等都运行在主线程中,不要把费时的工作放到主线程中,不然你会得到一个不响应UI和精疲力尽的猫.

三.孩子,不要在家做这个.

让我们看看现有的代码并且讨论它是怎么工作的.

app中的rootViewController是WebViewController,当你点grabTapped按钮,它获取当前页的html并传给imageListViewController.

在imageListViewController中的viewDidLoad中,它创建一个新的imageManager并处理之.这个类,还有imageInfo,包含了所有的费时代码.

让我们看看这两个类是如何工作的:

1.ImageManager:processHTML:用正则表达式匹配来搜索html中的链接.

2.ImageInfo:initWithSourceURL:调用getImage来获取图片,通过[NSData dataWithContentsOfURL:..]同步进行.

3.ImageInfo:retrieveZip:同2来获取zip文件

4.ImageInfo:processZip:调用zipArchieve库进行解压缩.

其它的一些方法就不介绍了.主要就是理解它如何工作的,以便让我们改进.

四.异步下载.

首先让我们用异步下载来代替这个最慢的操作--下载文件.

其实用苹果内建的NSURLRequest和NSURLConnection并不难,但是使用ASIHTTPRequest更简单.

下载和添加该类就不介绍了.

现在是时候进行替换了.

在ImageManger.m中:

 

// Add to top of file
#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];    
}

该代码实现了当异步下载后可能要执行的两个Block.

同理,在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];    
}

运行之,app马上转到detail tab,但是还有一个问题:图片没有显示.

五.介绍NSNotifications.

传更新从一段代码到另一段代码,使用NSNotification是一种很简单的方法.

1.当你完成一任务后想通知给其它代码段,调用postNotificationName.

2. 当你想知道更新是否完成时,使用addObserver:selector:name:object.

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];
 
}

这样,图片更新就完成了.

六.Grand Central Dispatch and Dispatch Queues.

这样还有一个问题:解压zip还在主线程中.

有空再翻译....

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter