自定义NSOperation的话,只是需要将要下载图片的操作下载它的main方法里面,考虑到,图片下载完毕,需要回传到控制器里,这里可以采用block,也可以采用代理的方式实现,我采用的是代理的方式实现的。
重点应该是如何避免同一个url的图片被重复下载?!事实上,可以有这样两个字典,key值是图片的url,value的话,一个字典可以是当前的operation对象,表示,这个url对应的图片正在下载,如果传入图片的url,value不为空的话,说明图片正在下载,那么就不需要再重复下载。另一个字典,可以是存放下载好的图片,也就是常说的缓存,如果一个url取出来的value不为nil,那么这张图片就存在缓存中,不需要再次下载。当然,别忘了设置占位图片~~
这是model的代码:
#import <Foundation/Foundation.h> @interface ZYApp : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *icon; @property (nonatomic, copy) NSString *download; + (id)appWithDict:(NSDictionary *)dict; - (id)initWithDict:(NSDictionary *)dict; @end #import "ZYApp.h" @implementation ZYApp - (id)initWithDict:(NSDictionary *)dict { if (self = [super init]){ [self setValuesForKeysWithDictionary:dict]; // NSLog(@"%@----%@---%@",self.icon,self.name,self.download); } return self; } + (id)appWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; } @end
这是自定义的Operation代码:
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @class ZYDownLoadImageOperation; @protocol ZYDownLoadImageOperationDelegate <NSObject> @optional - (void)DownLoadImageOperation:(ZYDownLoadImageOperation *)operation didFinishDownLoadImage:(UIImage *)image; @end @interface ZYDownLoadImageOperation : NSOperation @property (nonatomic, weak) id<ZYDownLoadImageOperationDelegate> delegate; @property (nonatomic, copy) NSString *url; @property (nonatomic, strong) NSIndexPath *indexPath; @end #import "ZYDownLoadImageOperation.h" @implementation ZYDownLoadImageOperation - (void)main //重写main方法即可 { @autoreleasepool { //在子线程中,并不会自动添加自动释放池,所以,手动添加,免得MARC的情况下,出现内存泄露的问题 NSURL *DownLoadUrl = [NSURL URLWithString:self.url]; if (self.isCancelled) return; //如果下载操作被取消,那么就无需下面操作了 NSData *data = [NSData dataWithContentsOfURL:DownLoadUrl]; if (self.isCancelled) return; UIImage *image = [UIImage imageWithData:data]; if (self.isCancelled) return; if ([self.delegate respondsToSelector:@selector(DownLoadImageOperation:didFinishDownLoadImage:)]) { dispatch_async(dispatch_get_main_queue(), ^{ //回到主线程,更新UI [self.delegate DownLoadImageOperation:self didFinishDownLoadImage:image]; }); } } } @end
这是控制器代码:
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end #import "ViewController.h" #import "ZYApp.h" #import "ZYDownLoadImageOperation.h" @interface ViewController () <UITableViewDataSource, UITableViewDelegate, ZYDownLoadImageOperationDelegate, UIScrollViewDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (nonatomic, strong) NSArray *apps; @property (nonatomic, strong) NSOperationQueue *queue; // key:图片的url values: 相对应的operation对象 (判断该operation下载操作是否正在执行,当同一个url地址的图片正在下载,那么不需要再次下载,以免重复下载,当下载操作执行完,需要移除) @property (nonatomic, strong) NSMutableDictionary *operations; // key:图片的url values: 相对应的图片 (缓存,当下载操作完成,需要将所下载的图片放到缓存中,以免同一个url地址的图片重复下载) @property (nonatomic, strong) NSMutableDictionary *images; @end @implementation ViewController #define ZYCellIdentifier @"ZYCellIdentifier" - (NSArray *)apps { if (_apps == nil) { NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; NSMutableArray *tmpArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { ZYApp *app = [ZYApp appWithDict:dict]; [tmpArray addObject:app]; } _apps = tmpArray; } return _apps; } - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [[NSOperationQueue alloc] init]; _queue.maxConcurrentOperationCount = 3; } return _queue; } - (NSMutableDictionary *)operations { if (_operations == nil) { _operations = [NSMutableDictionary dictionary]; } return _operations; } - (NSMutableDictionary *)images { if (_images == nil) { _images = [NSMutableDictionary dictionary]; } return _images; } - (void)viewDidLoad { [super viewDidLoad]; [self setupView]; } - (void)setupView { self.tableView.delegate = self; self.tableView.dataSource = self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.apps.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ZYCellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ZYCellIdentifier]; } ZYApp *app = self.apps[indexPath.row]; cell.textLabel.text = app.name; cell.detailTextLabel.text = app.download; UIImage *image = self.images[app.icon]; if (image) { cell.imageView.image = image; } else{ cell.imageView.image = [UIImage imageNamed:@"TestMam"]; ZYDownLoadImageOperation *operation = self.operations[app.icon]; if (operation) { //正在下载(可以在里面取消下载) } else{ //没有在下载 operation = [[ZYDownLoadImageOperation alloc] init]; operation.delegate = self; operation.url = app.icon; operation.indexPath = indexPath; [self.queue addOperation:operation]; //异步下载 self.operations[app.icon] = operation; //加入字典,表示正在执行此次操作 } } return cell; } #pragma mark -- ZYDownLoadImageOperationDelegate - (void)DownLoadImageOperation:(ZYDownLoadImageOperation *)operation didFinishDownLoadImage:(UIImage *)image { [self.operations removeObjectForKey:operation.url]; //下载操作完成,所以把它清掉,表示没有正在下载 if (image){ self.images[operation.url] = image; //下载完毕,放入缓存,免得重复下载 [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } #pragma mark --- UIScrollViewDelegate - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self.queue setSuspended:YES]; //在拖拽的时候,停止队列下载 } - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { [self.queue setSuspended:NO]; //在停止拖拽的时候,开始队列下载 } @end
这是此次mode代码github地址:https://github.com/wzpziyi1/CustomOperation