自定义NSOperation

本人录制技术视频地址:https://edu.csdn.net/lecturer/1899 欢迎观看。

上一节,我对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
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋恨雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值