一 概述
- 将NSBlockOperation封装成自定义Operation文件
- 将图片的下载放到自定义Operation文件的main方法中
- 下载后回到主线程image图片的显示,通过代理完成
二 自定义NSOperation
2.1 自定义NSOperation的步骤很简单
重写-(void)main
方法,在俩面实现想执行的任务
2.2 重写-(void)main的方法的注意点
- 自己创建自动释放池,因为如果是异步操作,无法访问主线程的自动释放池
- 经常通过-(BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
三 自定义NSOperation
3.1 DownloadOperation
DownloadOperation.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #import <Foundation/Foundation.h> #import <UIKit/UIKit.h>
@class DownloadOperation; @protocol ILDownloadOperationDelegate<NSObject>
@optional -(void)downloadOperation:(DownloadOperation *)operation didFinishDownload:(UIImage *)image andIndexPath:(NSIndexPath *)indexPath;
@end
@interface DownloadOperation : NSOperation
@property(nonatomic,copy) NSString *imageUrl; @property(nonatomic,strong) NSIndexPath *indexPath; @property(nonatomic,weak) id<ILDownloadOperationDelegate> delegate;
@end
|
DownloadOperation.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #import "DownloadOperation.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]; if (self.isCancelled) return; //判断下载过程中 //回到主线程 [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:andIndexPath:)]) { [self.delegate downloadOperation:self didFinishDownload:image andIndexPath:self.indexPath]; } }]; } } @end
|
3.2 App
App.h
1 2 3 4 5 6 7 8 9 10
| #import <Foundation/Foundation.h>
@interface App : NSObject @property(nonatomic,copy) NSString *name; @property(nonatomic,copy) NSString *download; @property(nonatomic,copy) NSString *icon;
+(instancetype)appWithDict:(NSDictionary *)dict;
@end
|
App.m
1 2 3 4 5 6 7 8 9 10 11
| #import "App.h"
@implementation App
+(instancetype)appWithDict:(NSDictionary *)dict { App *app=[[self alloc]init]; [app setValuesForKeysWithDictionary:dict]; return app; } @end
|
3.3 ILAppsViewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
| #define APPImageFile(url) [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:[url lastPathComponent]] #import "ILAppsViewController.h" #import "App.h" #import "DownloadOperation.h"
@interface ILAppsViewController ()<ILDownloadOperationDelegate> //存放数据 @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 ILAppsViewController
#pragma mark-懒加载代码
-(NSMutableArray *)apps { if (!_apps) { NSMutableArray *appArray=[NSMutableArray array]; //加载plist NSString *file=[[NSBundle mainBundle]pathForResource:@"apps" ofType:@"plist"]; NSArray *dicctArray=[NSArray arrayWithContentsOfFile:file]; //2-字典转模型 for(NSDictionary *dict in dicctArray){ App *app=[App appWithDict:dict]; [appArray addObject:app]; } //3.赋值 self.apps=appArray; } return _apps; } -(NSOperationQueue *)queue { if (!_queue) { self.queue=[[NSOperationQueue alloc]init]; } return _queue; } -(NSMutableDictionary *)operations { if (!_operations) { self.operations=[[NSMutableDictionary alloc]init]; } return _operations; } - (NSMutableDictionary *)images { if (!_images) { self.images=[[NSMutableDictionary alloc]init]; } return _images; }
- (void)viewDidLoad { [super viewDidLoad]; }
-(void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; //移除下载操作 [self.queue cancelAllOperations]; [self.images removeAllObjects]; [self.operations removeAllObjects]; } #pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { #warning Incomplete implementation, return the number of sections return 1; }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { #warning Incomplete implementation, return the number of rows 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; //NSLog(@"----从缓存中取得图片---%d",indexPath.row); }else{ //说明图片并未下载成功过(并未缓存过) NSString *file=APPImageFile(app.icon); //先从沙盒中取出文件 NSData *data=[NSData dataWithContentsOfFile:file]; if (data) {//沙盒中存在这个文件 cell.imageView.image=[UIImage imageWithData:data]; }else{ //沙盒中不存在这个文件 //NSLog(@"----缓存中没有图片---%d",indexPath.row); //显示占位图片 cell.imageView.image=[UIImage imageNamed:@"placeholder"]; //下载图片 [self download:app.icon indexPath:indexPath]; } } return cell; } -(void)download:(NSString *)imageUrl indexPath:(NSIndexPath *)indexPath{ //取出当前图片url对应的下载操作(operation对象) 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; } //当用户开始拖拽表格时调用开始拖拽 //1-会阻塞主线程,影响用户体验 //2-重复下载,浪费流量,浪费时间,影响用户体验 //保证:1张图片只下载1次 -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { //暂停下载 [self.queue setSuspended:YES]; } //当用户停止拖表格拽时 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { //恢复下载 [self.queue setSuspended:NO]; } -(void)downloadOperation:(DownloadOperation *)operation didFinishDownload:(UIImage *)image andIndexPath:(nonnull NSIndexPath *)indexPath { //存放图片到字典中 if(image){ //self.images[imageUrl]=image; self.images[operation.imageUrl]=image;//循环引用换成weak //将图片存入沙盒中:UIImage->Data——>File NSData *data=UIImagePNGRepresentation(image); [data writeToFile:APPImageFile(operation.imageUrl) atomically:YES]; } //从字典中移除下载操作 [self.operations removeObjectForKey:operation.imageUrl]; //刷新表格 //[self.tableView reloadData]; [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];//刷新一行 }
@end
|
3.4 效果图