🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
上一篇文章的下载图片操作都放在了block中,当遇到复杂的操作,一堆的代码放在block中 ,很明显这不是明智的选择,代码显得很臃肿。 因此,把线程操作放到自定义NSOperation中。 自定义NSOperation的步骤:继承NSOperation、重写- (void)main方法,在里面实现想执行的任务。 重写- (void)main方法的注意点: 1、自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)。 2、经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应。 案例代码: 1、新建DownloadOperation继承NSOperation,下载操作放到main方法中。 至于下载后回到主线程要做点什么,它自己本身不知道(不是它的工作范畴)。只负责下载完成后通知其它人,因此委托代理,委托别人去做事。 DownloadOperation.h ~~~ #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @class DownloadOperation; //协议 @protocol DownloadOperationDelegate <NSObject> @optional - (void)downloadOperation:(DownloadOperation *)operation didFinishDownload:(UIImage *)image; @end @interface DownloadOperation : NSOperation //下载图片地址 @property(nonatomic,copy) NSString *imageUrl; //表格cell位置 @property(nonatomic,strong) NSIndexPath *indexPath; //代理 @property(nonatomic,weak) id<DownloadOperationDelegate> delegate; @end ~~~ DownloadOperation.m       注意:@autoreleasepool 自动释放池 、isCancelled方法检测操作是否被取消,对取消做出响应。 ~~~ #import "DownloadOperation.h" #import <UIKit/UIKit.h> @implementation DownloadOperation -(void)main{ @autoreleasepool {//管理内存 if (self.isCancelled) return; //暂停为执行的操作 NSURL *url = [NSURL URLWithString:self.imageUrl]; NSData *data = [NSData dataWithContentsOfURL:url]; // 下载 UIImage *image = [UIImage imageWithData:data]; // NSData -> UIImage if(self.isCancelled) return;//暂停正在执行的操作 // 回到主线程 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:)]) { [self.delegate downloadOperation:self didFinishDownload:image]; } }]; } } @end ~~~ 2、AppsTableViewController.m ~~~ // // AppsTableViewController.m // cell图片下载(自定义operation) #import "AppsTableViewController.h" #import "App.h" #import "DownloadOperation.h" @interface AppsTableViewController ()<DownloadOperationDelegate> //应用信息集合 @property(nonatomic,strong) NSMutableArray *apps; //存放所有下载图片的队列 @property(nonatomic,strong) NSOperationQueue *queue; //存放所有的下载操作(url是key,operation对象是value) @property(nonatomic,strong) NSMutableDictionary *operations; //存放所有下载完的图片 @property(nonatomic,strong) NSMutableDictionary *images; @end @implementation AppsTableViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /** * 懒加载 **/ -(NSMutableArray *)apps{ if (!_apps) { NSMutableArray *appArr = [NSMutableArray array]; NSString *file =[[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:file]; for (NSDictionary *dict in dictArr) { App *app = [App appWithDict:dict]; [appArr addObject:app]; } _apps = appArr; } return _apps; } -(NSOperationQueue *)queue{ if (!_queue) { _queue = [[NSOperationQueue alloc]init]; } return _queue; } -(NSMutableDictionary *)operations{ if (!_operations) { _operations = [[NSMutableDictionary alloc]init]; } return _operations; } -(NSMutableDictionary *)images{ if (!_images) { _images = [[NSMutableDictionary alloc]init]; } return _images; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.apps.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *ID = @"app"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (!cell) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } App *app = self.apps[indexPath.row]; cell.textLabel.text = app.name; cell.detailTextLabel.text = app.download; // 先从images缓存中取出图片url对应的UIImage UIImage *image = self.images[app.icon]; if (image) {// 说明图片已经下载成功过(成功缓存) cell.imageView.image = image; }else{ // 获得caches的路径, 拼接文件路径 NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[app.icon lastPathComponent]]; // 先从沙盒中取出图片 NSData *data = [NSData dataWithContentsOfFile:file]; if (data) {//沙盒中存在图片 cell.imageView.image = [UIImage imageWithData:data]; }else{//沙盒不存,进行下载操作 //显示占位图片 cell.imageView.image = [UIImage imageNamed:@"placeholder"]; // 下载图片 [self download:app.icon indexPath:indexPath]; } } return cell; } -(void)download:(NSString *)imageUrl indexPath:(NSIndexPath *)indexPath{ //取出当前图片url对应下的下载操作(operations对象) DownloadOperation *operation = self.operations[imageUrl]; if (operation) return; //如果存在操作就不往下执行(因为可能该图片下载操作正在进行) //创建操作,下载图片 operation = [[DownloadOperation alloc]init]; operation.imageUrl = imageUrl; operation.indexPath = indexPath; //设置代理 operation.delegate = self; // 添加操作到队列中 [self.queue addOperation:operation]; // 添加到字典中 (这句代码为了解决重复下载) self.operations[imageUrl] = operation; } /** * 当用户开始拖拽表格时调用 */ -(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{ //暂停下载 [self.queue setSuspended:YES]; } /** * 当用户停止拖拽表格时调用 */ -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ //开始下载 [self.queue setSuspended:NO]; } #pragma mark - 下载操作的代理方法 -(void)downloadOperation:(DownloadOperation *)operation didFinishDownload:(UIImage *)image{ // 存放图片到字典中 if (image) { //存放所有的下载操作 self.operations[operation.imageUrl] = image; //将图片存入沙盒中 NSData *data = UIImagePNGRepresentation(image); NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:[operation.imageUrl lastPathComponent]]; [data writeToFile:file atomically:YES]; } // 从字典中移除下载操作 (防止operations越来越大,保证下载失败后,能重新下载) [self.operations removeObjectForKey:operation.imageUrl]; // 刷新表格 [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone]; } @end ~~~