标签:多线程 并行编程 异步 nsoperation
上一节,我对NSOperation的基本概念及使用进行了介绍,想要了解的,请点击这里。本节中,我介绍自定义NSOperation实现多线程异步下载图片,类似于SDWebImage。
自定义NSOperation的步骤很简单,重写 - (void)main方法,在里面实现想执行的任务。
重写 - (void)main方法注意点:
1.自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
2.经常通过 - (BOOL)isCancelled 方法检测操作是否被取消,对取消做出响应。
Demo最终的效果图:
上面的图片均是通过NSOperation异步下载的。
下面,我具体介绍代码的实现过程。
1.首先,我准备了数据源(data.plist文件)
2. 定义了对应的数据模型
@interface DataModel : NSObject /* 图片名称 */ @property (nonatomic,copy) NSString *name; /* 图片url地址 */ @property (nonatomic,copy) NSString *url; /* 下载次数 */ @property (nonatomic,strong) NSNumber *downloadedCount; // 字典转化为模型 - (instancetype)initWithDict:(NSDictionary *)dict; @end @implementation DataModel - (instancetype)initWithDict:(NSDictionary *)dict { if (self = [super init]) { // (KVC)字典转模型 [self setValuesForKeysWithDictionary:dict]; } return self; } @end
#import "ViewController.h" #import "DataModel.h" #import "LFDownloadOperation.h" @interface ViewController ()<LFDownloadOperationDelegate> /* 数据源 */ @property (nonatomic,strong) NSMutableArray *dataLists; // Key:url, Value: UIImage (存放图片的缓存字典,有的话,直接取出使用;没有的话,下载) @property (nonatomic,strong) NSMutableDictionary *images; // Key:url, Value: LFDownloadOperation @property (nonatomic,strong) NSMutableDictionary *operations; // 下载队列,存放LFDownloadOperation @property (nonatomic,strong) NSOperationQueue *queue; @end @implementation ViewController #pragma mark - Lazy Load - (NSMutableArray *)dataLists { if (!_dataLists) { NSString *file = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"plist"]; NSArray *dataArr = [NSArray arrayWithContentsOfFile:file]; NSMutableArray *tempLists = [NSMutableArray array]; for (NSDictionary *dict in dataArr) { DataModel *model = [[DataModel alloc] initWithDict:dict]; [tempLists addObject:model]; } _dataLists = tempLists; } return _dataLists; } - (NSMutableDictionary *)images { if (!_images) { _images = [NSMutableDictionary dictionary]; } return _images; } - (NSMutableDictionary *)operations { if( !_operations) { _operations = [NSMutableDictionary dictionary]; } return _operations; } - (NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueue alloc] init]; } return _queue; } - (void)viewDidLoad { [super viewDidLoad]; } #pragma mark - UITableView Delegate/DataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.dataLists.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifer = @"UITableViewCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifer]; } DataModel *model = self.dataLists[indexPath.row]; cell.textLabel.text = model.name; cell.detailTextLabel.text = [model.downloadedCount stringValue]; // 主要实现下载的代码 UIImage *image = self.images[model.url]; if (image) { // 图片下载完毕(在字典中) cell.imageView.image = image; } else { // 创建下载Operation并设置默认占位图片 LFDownloadOperation *operation = self.operations[model.url]; cell.imageView.image = [UIImage imageNamed:@"placeholder.jpg"]; // 如果operation有值,说明正在下载 if (!operation) { // 还没有下载,立刻创建operation并下载 operation = [[LFDownloadOperation alloc] init]; operation.delegate = self; operation.url = model.url; operation.indexPath = indexPath; // 将每张图片的下载地址和每个opearion建立对应关系 self.operations[model.url] = operation; // 不能在这里创建队列,应该只创建一个队列(所以使用懒加载) //NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 加到队列中并下载 [self.queue addOperation:operation]; } } return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 130; } #pragma mark - LFDownloadOperationDelegate - (void)downloadOperation:(LFDownloadOperation *)operation didFinishedWithImage:(UIImage *)image { NSIndexPath *indexPath = operation.indexPath; DataModel *model = self.dataLists[indexPath.row]; // 当一张图片下载完成了,则应该移除这张图片对应的operation [self.operations removeObjectForKey:model.url]; // 将下载完成的图片保存到images这个字典缓存池中 self.images[model.url] = image; // 每次有图片下载完成,则刷新对应的cell并显示图片 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } @end
@class LFDownloadOperation; @protocol LFDownloadOperationDelegate <NSObject> @optional /* 代理返回下载完毕的图片 */ - (void)downloadOperation:(LFDownloadOperation *)operation didFinishedWithImage:(UIImage *)image; @end @interface LFDownloadOperation : NSOperation @property (nonatomic,copy) NSString *url; @property (nonatomic,strong) NSIndexPath *indexPath; @property (nonatomic,assign) id<LFDownloadOperationDelegate> delegate; @end @implementation LFDownloadOperation // NSOperation中的main就是实现相应的异步操作,所以重写父类方法 - (void)main { // 因为LFDownloadOperation已经不在主线程的自动释放池了,所以要包含在@autoreleasepool中 @autoreleasepool { // 取消操作发生在任何时刻都有可能,因此在执行任何操作之前,先检测该操作是否已经被取消 if (self.isCancelled) { return; } NSURL *url = [NSURL URLWithString:self.url]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; if (self.isCancelled) { return; } if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishedWithImage:)]){ dispatch_async(dispatch_get_main_queue(), ^{ // 最后将异步下载完成的图片返回到主线程(dispatch_get_main_queue) [self.delegate downloadOperation:self didFinishedWithImage:image]; }); } } } @end
标签:多线程 并行编程 异步 nsoperation
原文地址:http://blog.csdn.net/sinat_27706697/article/details/45688573