自定义NSOperation

来源:互联网 发布:电话号码数据 编辑:程序博客网 时间:2024/05/22 03:30

上一节,我对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

3. ViewController中得代码:

#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

4. 自定义的NSOperation

@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


0 0