一、基础用法
1 // 2 // ViewController.m 3 // IOS_0120_NSOperation 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 11 @interface ViewController ()<UITableViewDelegate> 12 13 @property (nonatomic, strong) UIImageView *imageView; 14 15 @end 16 17 @implementation ViewController 18 /* 19 一、简介 20 1.NSOperation的作用 21 配合使用NSOperation和NSOperationQueue也能实现多线程编程 22 23 2.NSOperation和NSOperationQueue实现多线程的具体步骤 24 1>先将需要执行的操作封装到一个NSOperation对象中 25 2>然后将NSOperation对象添加到NSOperationQueue中 26 3>系统会自动将NSOperationQueue中的NSOperation取出来 27 4>将取出的NSOperation封装的操作放到一条新线程中执行 28 29 二、NSOperation的子类 30 1.NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类 31 32 2.使用NSOperation子类的方式有3种 33 1>NSInvocationOperation 34 2>NSBlockOperation 35 3>自定义子类继承NSOperation,实现内部相应的方法 36 37 三、具体使用 38 1.NSInvocationOperation 39 1>创建NSInvocationOperation对象 40 - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg; 41 42 2>调用start方法开始执行操作 43 - (void)start; 44 一旦执行操作,就会调用target的sel方法 45 46 3>注意 47 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作 48 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作 49 50 2.NSBlockOperation 51 1>创建NSBlockOperation对象 52 + (id)blockOperationWithBlock:(void (^)(void))block; 53 54 2>通过addExecutionBlock:方法添加更多的操作 55 - (void)addExecutionBlock:(void (^)(void))block; 56 57 3>注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作 58 59 三、NSOperationQueue 60 1.NSOperationQueue的作用 61 NSOperation可以调用start方法来执行任务,但默认是同步执行的 62 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作 63 64 2.添加操作到NSOperationQueue中 65 - (void)addOperation:(NSOperation *)op; 66 - (void)addOperationWithBlock:(void (^)(void))block; 67 68 四、最大并发数 69 1.什么是并发数 70 同时执行的任务数 71 比如,同时开3个线程执行3个任务,并发数就是3 72 73 2.最大并发数的相关方法 74 - (NSInteger)maxConcurrentOperationCount; 75 - (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 76 77 五、队列的取消、暂停、恢复 78 取消队列的所有操作 79 - (void)cancelAllOperations; 80 提示:也可以调用NSOperation的- (void)cancel方法取消单个操作 81 82 暂停和恢复队列 83 - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列 84 - (BOOL)isSuspended; 85 86 六、操作优先级 87 1.设置NSOperation在queue中的优先级,可以改变操作的执行优先级 88 - (NSOperationQueuePriority)queuePriority; 89 - (void)setQueuePriority:(NSOperationQueuePriority)p; 90 91 2.优先级的取值 92 NSOperationQueuePriorityVeryLow = -8L, 93 NSOperationQueuePriorityLow = -4L, 94 NSOperationQueuePriorityNormal = 0, 95 NSOperationQueuePriorityHigh = 4, 96 NSOperationQueuePriorityVeryHigh = 8 97 98 七、操作的监听 99 1.可以监听一个操作的执行完毕 100 - (void (^)(void))completionBlock; 101 - (void)setCompletionBlock:(void (^)(void))block; 102 103 八、操作的依赖 104 NSOperation之间可以设置依赖来保证执行顺序 105 比如一定要让操作A执行完后,才能执行操作B,可以这么写 106 [operationB addDependency:operationA]; // 操作B依赖于操作A 107 可以在不同queue的NSOperation之间创建依赖关系 108 109 注意:不能相互依赖 比如A依赖B,B依赖A 110 111 九、第三方框架的使用建议 112 1.用第三方框架的目的 113 1> 开发效率:快速开发,人家封装好的一行代码顶自己写的N行 114 2> 为了使用这个功能最牛逼的实现 115 116 2.第三方框架过多,很多坏处(忽略不计) 117 1> 管理、升级、更新 118 2> 第三方框架有BUG,等待作者解决 119 3> 第三方框架的作者不幸去世、停止更新(潜在的BUG无人解决) 120 4> 感觉:自己好水 121 122 3.比如 123 流媒体:播放在线视频、音频(边下载边播放) 124 非常了解音频、视频文件的格式 125 每一种视频都有自己的解码方式(CC++) 126 127 4.总结 128 1> 站在巨人的肩膀上编程 129 2> 没有关系,使劲用那么比较稳定的第三方框架 130 131 十、SDWebImage 132 1.什么是SDWebImage 133 iOS中著名的牛逼的网络图片处理框架 134 包含的功能:图片下载、图片缓存、下载进度监听、gif处理等等 135 用法极其简单,功能十分强大,大大提高了网络图片的处理效率 136 国内超过90%的iOS项目都有它的影子 137 2.项目地址 138 https://github.com/rs/SDWebImage 139 140 1> 常用方法 141 - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; 142 - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; 143 - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock; 144 - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock; 145 146 2> 内存处理:当app接收到内存警告时 147 当app接收到内存警告 148 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application 149 { 150 SDWebImageManager *mgr = [SDWebImageManager sharedManager]; 151 152 // 1.取消正在下载的操作 153 [mgr cancelAll]; 154 155 // 2.清除内存缓存 156 [mgr.imageCache clearMemory]; 157 } 158 3> SDWebImageOptions 159 * SDWebImageRetryFailed : 下载失败后,会自动重新下载 160 * SDWebImageLowPriority : 当正在进行UI交互时,自动暂停内部的一些下载操作 161 * SDWebImageRetryFailed | SDWebImageLowPriority : 拥有上面2个功能 162 163 十一、自定义NSOperation 164 自定义NSOperation的步骤很简单 165 重写- (void)main方法,在里面实现想执行的任务 166 167 重写- (void)main方法的注意点 168 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池) 169 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应 170 171 */ 172 173 - (void)viewDidLoad { 174 [super viewDidLoad]; 175 176 // [self useNSInvocationOperation]; 177 // [self useBaseNSBlockOperation]; 178 // [self useNSBlockOperation]; 179 // [self useNSOperationQueue]; 180 // [self useAddDependency]; 181 182 self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(30, 200, 340, 200)]; 183 [self.view addSubview:self.imageView]; 184 [self communicate]; 185 186 } 187 188 #pragma mark - 通讯 189 - (void)communicate 190 { 191 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 192 //取消队列所有操作 193 // [queue cancelAllOperations]; 194 // NSLog(@"didCancelAllOperations"); 195 //暂停队列 196 [queue setSuspended:YES]; 197 //恢复队列 198 [queue setSuspended:NO]; 199 //异步下载图片 200 [queue addOperationWithBlock:^{ 201 NSURL *url = [NSURL URLWithString:@"http://images.haiwainet.cn/2016/0113/20160113015030150.jpg"]; 202 NSData *data = [[NSData alloc] initWithContentsOfURL:url]; 203 UIImage *image = [UIImage imageWithData:data]; 204 //回到主线程,显示图片 205 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 206 self.imageView.image = image; 207 }]; 208 }]; 209 } 210 211 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 212 { 213 // [queue setSuspended:YES]; //暂停队列中所有任务 214 } 215 216 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 217 { 218 // [queue setSuspended:NO]; //恢复队列中所有任务 219 } 220 221 #pragma mark - 依赖 222 - (void)useAddDependency 223 { 224 /*三个异步执行操作:操作C依赖于操作B,操作B依赖于操作A*/ 225 226 //创建一个队列 227 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 228 229 NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{ 230 NSLog(@"A1--------%@",[NSThread currentThread]); 231 }]; 232 [operationA addExecutionBlock:^{ 233 NSLog(@"A2--------%@",[NSThread currentThread]); 234 }]; 235 [operationA setCompletionBlock:^{ 236 NSLog(@"依赖于A--------%@",[NSThread currentThread]); 237 238 }]; 239 NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{ 240 NSLog(@"B--------%@",[NSThread currentThread]); 241 }]; 242 NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{ 243 NSLog(@"C--------%@",[NSThread currentThread]); 244 }]; 245 //设置依赖 246 [operationB addDependency:operationA]; 247 [operationC addDependency:operationB]; 248 249 [queue addOperation:operationA]; 250 [queue addOperation:operationB]; 251 [queue addOperation:operationC]; 252 } 253 254 #pragma mark - 队列中直接添加任务 255 - (void)useNSOperationQueue 256 { 257 //1.创建operation的时候,添加一个任务 258 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ 259 NSLog(@"--------下载图片1--------%@",[NSThread currentThread]); 260 }]; 261 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ 262 NSLog(@"--------下载图片2--------%@",[NSThread currentThread]); 263 }]; 264 265 //2.创建队列(非主队列)会自动异步执行任务,并发 266 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 267 268 //3.设置最大并发数 269 queue.maxConcurrentOperationCount = 2; 270 271 //4.添加操作到队列中 272 [queue addOperation:operation1]; 273 [queue addOperation:operation2]; 274 275 [queue addOperationWithBlock:^{ 276 NSLog(@"--------下载图片3--------%@",[NSThread currentThread]); 277 }]; 278 } 279 280 #pragma mark - 多操作添加到队列 281 - (void)useNSBlockOperation 282 { 283 //创建operation的时候,添加一个任务 284 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ 285 NSLog(@"--------下载图片1--------%@",[NSThread currentThread]); 286 }]; 287 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ 288 NSLog(@"--------下载图片2--------%@",[NSThread currentThread]); 289 }]; 290 //创建队列 291 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 292 //主队列 293 // NSOperationQueue *queue = [NSOperationQueue mainQueue]; 294 //添加操作到队列中 295 [queue addOperation:operation1]; 296 [queue addOperation:operation2]; 297 298 //[operation start]; 299 } 300 301 #pragma mark - NSBlockOperation 302 - (void)useBaseNSBlockOperation 303 { 304 //创建operation的时候,添加一个任务 305 // NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 306 // NSLog(@"--------下载图片1--------%@",[NSThread currentThread]); 307 // }]; 308 309 NSBlockOperation *operation = [[NSBlockOperation alloc] init]; 310 //操作中添加任务 311 [operation addExecutionBlock:^{ 312 NSLog(@"--------下载图片2--------%@",[NSThread currentThread]); 313 }]; 314 [operation addExecutionBlock:^{ 315 NSLog(@"--------下载图片3--------%@",[NSThread currentThread]); 316 }]; 317 318 //任务大于1,才会异步执行 319 [operation start]; 320 } 321 322 #pragma mark - NSInvocationOperation 323 - (void)useNSInvocationOperation 324 { 325 //创建队列 326 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 327 //创建操作 328 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil]; 329 330 //opreation直接调用start,是同步执行(在当前线程执行操作) 331 //[operation start]; 332 333 //添加操作到队列中,会自动异步执行 334 [queue addOperation:operation]; 335 } 336 337 - (void)download 338 { 339 NSLog(@"download-------%@",[NSThread currentThread]); 340 } 341 342 - (void)didReceiveMemoryWarning { 343 [super didReceiveMemoryWarning]; 344 // Dispose of any resources that can be recreated. 345 } 346 347 @end
二、防止重复下载,与沙盒缓存
1 // 2 // BWApp.h 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface BWApp : NSObject 12 13 @property (nonatomic, copy) NSString *name; 14 @property (nonatomic, copy) NSString *download; 15 @property (nonatomic, copy) NSString *icon; 16 17 + (instancetype)appWithDict:(NSDictionary *)dict; 18 19 @end 20 21 // 22 // BWApp.m 23 // IOS_0120_NSOperation防止重复下载 24 // 25 // Created by ma c on 16/1/20. 26 // Copyright © 2016年 博文科技. All rights reserved. 27 // 28 29 #import "BWApp.h" 30 31 @implementation BWApp 32 33 + (instancetype)appWithDict:(NSDictionary *)dict 34 { 35 BWApp *app = [[self alloc] init]; 36 [app setValuesForKeysWithDictionary:dict]; 37 return app; 38 } 39 40 @end
1 // 2 // BWTableViewController.m 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import "BWTableViewController.h" 10 #import "BWApp.h" 11 12 #define appImageCachesPath(url) [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[url lastPathComponent]] 13 14 @interface BWTableViewController () 15 16 //所有应用数据 17 @property (nonatomic, strong) NSMutableArray *appsArray; 18 //存放所有下载操作的队列 19 @property (nonatomic, strong) NSOperationQueue *queue; 20 //存放所有下载操作(url是key,operation对象是value) 21 @property (nonatomic, strong) NSMutableDictionary *operationDict; 22 //缓存图片 23 @property (nonatomic, strong) NSMutableDictionary *imageDict; 24 25 @end 26 27 @implementation BWTableViewController 28 29 #pragma mark - 懒加载 30 - (NSMutableArray *)appsArray 31 { 32 if (!_appsArray) { 33 NSMutableArray *appArr = [[NSMutableArray alloc] init]; 34 35 NSString *path = [[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"]; 36 37 NSArray *dictArr = [NSArray arrayWithContentsOfFile:path]; 38 39 for (NSDictionary *dict in dictArr) { 40 41 BWApp *app = [BWApp appWithDict:dict]; 42 [appArr addObject:app]; 43 } 44 _appsArray = appArr; 45 } 46 return _appsArray; 47 } 48 49 - (NSOperationQueue *)queue 50 { 51 if (!_queue) { 52 _queue = [[NSOperationQueue alloc] init]; 53 } 54 return _queue; 55 } 56 57 - (NSMutableDictionary *)operationDict 58 { 59 if (!_operationDict) { 60 _operationDict = [[NSMutableDictionary alloc] init]; 61 } 62 return _operationDict; 63 } 64 65 - (NSMutableDictionary *)imageDict 66 { 67 if (!_imageDict) { 68 _imageDict = [[NSMutableDictionary alloc] init]; 69 } 70 return _imageDict; 71 } 72 73 #pragma mark - 初始化 74 - (void)viewDidLoad { 75 [super viewDidLoad]; 76 77 } 78 79 - (void)didReceiveMemoryWarning 80 { 81 [super didReceiveMemoryWarning]; 82 83 //移除所有下载操作 84 [self.queue cancelAllOperations]; 85 [self.operationDict removeAllObjects]; 86 //移除所有图片缓存 87 [self.imageDict removeAllObjects]; 88 } 89 90 91 #pragma mark - Table view data source 92 93 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 94 95 return 1; 96 } 97 98 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 99 100 return self.appsArray.count; 101 } 102 103 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 104 static NSString *cellID = @"cellID"; 105 106 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 107 108 if (!cell) { 109 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; 110 } 111 //取出模型 112 BWApp *app = [self.appsArray objectAtIndex:indexPath.row]; 113 114 cell.textLabel.text = app.name; 115 cell.detailTextLabel.text = app.download; 116 117 //先从imageDict缓存中取出图片url对应的image 118 UIImage *image = self.imageDict[app.icon]; 119 120 if (image) { //说明图片已经下载成功过 121 cell.imageView.image = image; 122 } 123 else //说明图片未下载(没有缓存) 124 { 125 //获取caches路径,拼接文件路径 126 NSString *filePath = appImageCachesPath(app.icon); 127 128 NSData *data = [NSData dataWithContentsOfFile:filePath]; 129 130 if (data) { //沙盒中存在这个图片 131 cell.imageView.image = [UIImage imageWithData:data]; 132 } 133 else{ 134 //显示占位图片 135 cell.imageView.image = [UIImage imageNamed:@"placeholder"]; 136 } 137 138 [self download:app.icon andIndexPath:indexPath]; 139 } 140 return cell; 141 } 142 143 - (void)download:(NSString *)imageUrl andIndexPath:(NSIndexPath *)indexPath 144 { 145 //取出当前图片对应的下载操作(operation对象) 146 NSBlockOperation *operation = self.operationDict[imageUrl]; 147 148 if (operation) return; 149 150 // __weak BWTableViewController *vc = self; 151 __weak typeof(self) appVC = self; 152 153 //创建操作,下载图片 154 operation = [NSBlockOperation blockOperationWithBlock:^{ 155 NSURL *url = [NSURL URLWithString:imageUrl]; 156 NSData *data = [NSData dataWithContentsOfURL:url]; 157 UIImage *image = [UIImage imageWithData:data]; 158 159 //回到主线程 160 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 161 162 //判断图片是否存在 163 if (image) { 164 //存放图片到字典中 165 appVC.imageDict[imageUrl] = image; 166 #warning 沙盒缓存 167 //将图片存入沙盒之中 UIImage --> NSData --> File(文件) 168 NSData *data = UIImagePNGRepresentation(image); 169 170 // //获取caches路径 171 // NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; 172 // 173 // //拼接文件路径 174 // NSString *fileName = [imageUrl lastPathComponent]; 175 // NSString *filePath = [cachesPath stringByAppendingPathComponent:fileName]; 176 177 //写入缓存 178 [data writeToFile:appImageCachesPath(imageUrl) atomically:YES]; 179 180 //UIImageJPEGRepresentation(UIImage * _Nonnull image, CGFloat compressionQuality) 181 } 182 //从字典中移除下载操作 183 [appVC.operationDict removeObjectForKey:imageUrl]; 184 //刷新表格 185 //[self.tableView reloadData]; 186 [appVC.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 187 }]; 188 }]; 189 //添加操作到队列中 190 [appVC.queue addOperation:operation]; 191 //添加操作到字典中(为了解决重复下载) 192 appVC.operationDict[imageUrl] = operation; 193 } 194 //保证图片只下载过一次 195 //当用户开始拖拽表格时 196 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 197 { 198 //暂停下载 199 [self.queue setSuspended:YES]; 200 } 201 //用户停止拖拽表格时 202 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 203 { 204 //恢复下载 205 [self.queue setSuspended:NO]; 206 } 207 208 209 @end
三、自定义NSOperation
1 // 2 // BWApp.h 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface BWApp : NSObject 12 13 @property (nonatomic, copy) NSString *name; 14 @property (nonatomic, copy) NSString *download; 15 @property (nonatomic, copy) NSString *icon; 16 17 + (instancetype)appWithDict:(NSDictionary *)dict; 18 19 @end 20 21 22 // 23 // BWApp.m 24 // IOS_0120_NSOperation防止重复下载 25 // 26 // Created by ma c on 16/1/20. 27 // Copyright © 2016年 博文科技. All rights reserved. 28 // 29 30 #import "BWApp.h" 31 32 @implementation BWApp 33 34 + (instancetype)appWithDict:(NSDictionary *)dict 35 { 36 BWApp *app = [[self alloc] init]; 37 [app setValuesForKeysWithDictionary:dict]; 38 return app; 39 } 40 41 @end
1 // 2 // BWDownloadOperation.h 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/21. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import <UIKit/UIKit.h> 11 12 @class BWDownloadOperation; 13 14 @protocol BWDownloadOperationDelegate <NSObject> 15 16 @optional 17 - (void)downloadOperation:(BWDownloadOperation *)operation didFinishDownload:(UIImage *)image; 18 19 @end 20 21 @interface BWDownloadOperation : NSOperation 22 23 @property (nonatomic, copy) NSString *imageUrl; 24 @property (nonatomic, strong) NSIndexPath *indexPath; 25 26 @property (nonatomic, weak) id<BWDownloadOperationDelegate> delegate; 27 28 29 @end 30 31 32 // 33 // BWDownloadOperation.m 34 // IOS_0120_NSOperation防止重复下载 35 // 36 // Created by ma c on 16/1/21. 37 // Copyright © 2016年 博文科技. All rights reserved. 38 // 39 40 #import "BWDownloadOperation.h" 41 42 @implementation BWDownloadOperation 43 44 - (void)main 45 { 46 @autoreleasepool { 47 48 if (self.isCancelled) return; 49 50 //创建操作,下载图片 51 NSURL *url = [NSURL URLWithString:self.imageUrl]; 52 NSData *data = [NSData dataWithContentsOfURL:url]; 53 UIImage *image = [UIImage imageWithData:data]; 54 55 if (self.isCancelled) return; 56 57 //回到主线程 58 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 59 if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:)]) { 60 [self.delegate downloadOperation:self didFinishDownload:image]; 61 } 62 }]; 63 } 64 } 65 @end
1 // 2 // BWTableViewController.m 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import "BWTableViewController.h" 10 #import "BWApp.h" 11 #import "BWDownloadOperation.h" 12 13 #define appImageCachesPath(url) [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[url lastPathComponent]] 14 15 @interface BWTableViewController ()<BWDownloadOperationDelegate> 16 17 //所有应用数据 18 @property (nonatomic, strong) NSMutableArray *appsArray; 19 //存放所有下载操作的队列 20 @property (nonatomic, strong) NSOperationQueue *queue; 21 //存放所有下载操作(url是key,operation对象是value) 22 @property (nonatomic, strong) NSMutableDictionary *operationDict; 23 //缓存图片 24 @property (nonatomic, strong) NSMutableDictionary *imageDict; 25 26 @end 27 28 @implementation BWTableViewController 29 30 #pragma mark - 懒加载 31 - (NSMutableArray *)appsArray 32 { 33 if (!_appsArray) { 34 NSMutableArray *appArr = [[NSMutableArray alloc] init]; 35 36 NSString *path = [[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"]; 37 38 NSArray *dictArr = [NSArray arrayWithContentsOfFile:path]; 39 40 for (NSDictionary *dict in dictArr) { 41 42 BWApp *app = [BWApp appWithDict:dict]; 43 [appArr addObject:app]; 44 } 45 _appsArray = appArr; 46 } 47 return _appsArray; 48 } 49 50 - (NSOperationQueue *)queue 51 { 52 if (!_queue) { 53 _queue = [[NSOperationQueue alloc] init]; 54 } 55 return _queue; 56 } 57 58 - (NSMutableDictionary *)operationDict 59 { 60 if (!_operationDict) { 61 _operationDict = [[NSMutableDictionary alloc] init]; 62 } 63 return _operationDict; 64 } 65 66 - (NSMutableDictionary *)imageDict 67 { 68 if (!_imageDict) { 69 _imageDict = [[NSMutableDictionary alloc] init]; 70 } 71 return _imageDict; 72 } 73 74 #pragma mark - 初始化 75 - (void)viewDidLoad { 76 [super viewDidLoad]; 77 78 } 79 80 - (void)didReceiveMemoryWarning 81 { 82 [super didReceiveMemoryWarning]; 83 84 //移除所有下载操作 85 [self.queue cancelAllOperations]; 86 [self.operationDict removeAllObjects]; 87 //移除所有图片缓存 88 [self.imageDict removeAllObjects]; 89 } 90 91 92 #pragma mark - Table view data source 93 94 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 95 96 return 1; 97 } 98 99 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 100 101 return self.appsArray.count; 102 } 103 104 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 105 static NSString *cellID = @"cellID"; 106 107 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 108 109 if (!cell) { 110 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; 111 } 112 //取出模型 113 BWApp *app = [self.appsArray objectAtIndex:indexPath.row]; 114 115 cell.textLabel.text = app.name; 116 cell.detailTextLabel.text = app.download; 117 118 //先从imageDict缓存中取出图片url对应的image 119 UIImage *image = self.imageDict[app.icon]; 120 121 if (image) { //说明图片已经下载成功过 122 cell.imageView.image = image; 123 } 124 else //说明图片未下载(没有缓存) 125 { 126 //获取caches路径,拼接文件路径 127 NSString *filePath = appImageCachesPath(app.icon); 128 129 NSData *data = [NSData dataWithContentsOfFile:filePath]; 130 131 if (data) { //沙盒中存在这个图片 132 cell.imageView.image = [UIImage imageWithData:data]; 133 } 134 else{ 135 //显示占位图片 136 cell.imageView.image = [UIImage imageNamed:@"placeholder"]; 137 } 138 139 [self download:app.icon andIndexPath:indexPath]; 140 } 141 return cell; 142 } 143 144 - (void)download:(NSString *)imageUrl andIndexPath:(NSIndexPath *)indexPath 145 { 146 //取出当前图片对应的下载操作(operation对象) 147 BWDownloadOperation *operation = self.operationDict[imageUrl]; 148 149 if (operation) return; 150 151 //创建操作下载图片 152 operation = [[BWDownloadOperation alloc] init]; 153 operation.imageUrl = imageUrl; 154 operation.indexPath = indexPath; 155 156 //设置代理 157 operation.delegate = self; 158 159 //添加操作到队列中 160 [self.queue addOperation:operation]; 161 162 //添加操作到字典中(为了解决重复下载) 163 self.operationDict[imageUrl] = operation; 164 165 } 166 167 #pragma mark - 下载代理方法 168 169 - (void)downloadOperation:(BWDownloadOperation *)operation didFinishDownload:(UIImage *)image 170 { 171 172 173 //UIImageJPEGRepresentation(<#UIImage * _Nonnull image#>, <#CGFloat compressionQuality#>) 174 175 //判断图片是否存在 176 if (image) { 177 //存放图片到字典中 178 self.imageDict[operation.imageUrl] = image; 179 180 #warning 沙盒缓存 181 //将图片存入沙盒之中 UIImage --> NSData --> File(文件) 182 NSData *data = UIImagePNGRepresentation(image); 183 184 //写入缓存 185 [data writeToFile:appImageCachesPath(operation.imageUrl) atomically:YES]; 186 187 } 188 //从字典中移除下载操作(防止operationDict的下载操作越来越大,保证下载失败后能重新下载) 189 [self.operationDict removeObjectForKey:operation.imageUrl]; 190 //刷新表格 191 //[self.tableView reloadData]; 192 [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone]; 193 } 194 195 196 //保证图片只下载过一次 197 //当用户开始拖拽表格时 198 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 199 { 200 //暂停下载 201 [self.queue setSuspended:YES]; 202 } 203 //用户停止拖拽表格时 204 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 205 { 206 //恢复下载 207 [self.queue setSuspended:NO]; 208 } 209 210 211 @end
四、SDWebImage
1 // 2 // BWApp.h 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface BWApp : NSObject 12 13 @property (nonatomic, copy) NSString *name; 14 @property (nonatomic, copy) NSString *download; 15 @property (nonatomic, copy) NSString *icon; 16 17 + (instancetype)appWithDict:(NSDictionary *)dict; 18 19 @end 20 21 22 // 23 // BWApp.m 24 // IOS_0120_NSOperation防止重复下载 25 // 26 // Created by ma c on 16/1/20. 27 // Copyright © 2016年 博文科技. All rights reserved. 28 // 29 30 #import "BWApp.h" 31 32 @implementation BWApp 33 34 + (instancetype)appWithDict:(NSDictionary *)dict 35 { 36 BWApp *app = [[self alloc] init]; 37 [app setValuesForKeysWithDictionary:dict]; 38 return app; 39 } 40 41 @end
1 // 2 // BWTableViewController.m 3 // IOS_0120_NSOperation防止重复下载 4 // 5 // Created by ma c on 16/1/20. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import "BWTableViewController.h" 10 #import "BWApp.h" 11 #import "UIImageView+WebCache.h" 12 13 @interface BWTableViewController () 14 15 //所有应用数据 16 @property (nonatomic, strong) NSMutableArray *appsArray; 17 18 @end 19 20 @implementation BWTableViewController 21 22 #pragma mark - 懒加载 23 - (NSMutableArray *)appsArray 24 { 25 if (!_appsArray) { 26 NSMutableArray *appArr = [[NSMutableArray alloc] init]; 27 28 NSString *path = [[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"]; 29 30 NSArray *dictArr = [NSArray arrayWithContentsOfFile:path]; 31 32 for (NSDictionary *dict in dictArr) { 33 34 BWApp *app = [BWApp appWithDict:dict]; 35 [appArr addObject:app]; 36 } 37 _appsArray = appArr; 38 } 39 return _appsArray; 40 } 41 42 #pragma mark - 初始化 43 - (void)viewDidLoad { 44 [super viewDidLoad]; 45 46 } 47 48 - (void)didReceiveMemoryWarning 49 { 50 [super didReceiveMemoryWarning]; 51 } 52 53 #pragma mark - Table view data source 54 55 //- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 56 // 57 // return 1; 58 //} 59 60 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 61 62 // return self.appsArray.count; 63 return 1; 64 } 65 66 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 67 68 static NSString *cellID = @"cellID"; 69 70 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 71 72 if (!cell) { 73 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; 74 } 75 //取出模型 76 BWApp *app = [self.appsArray objectAtIndex:indexPath.row]; 77 //设置基本信息 78 cell.textLabel.text = app.name; 79 cell.detailTextLabel.text = app.download; 80 81 //下载图片 82 // NSURL *url = [NSURL URLWithString:@"http://img.cnblogs.com/ad/not-to-stop-questioning.jpg"]; 83 84 NSURL *url = [NSURL URLWithString:app.icon]; 85 UIImage *image = [UIImage imageNamed:@"placeholder"]; 86 // [cell.imageView sd_setImageWithURL:url placeholderImage:image]; 87 88 SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageLowPriority; 89 90 [cell.imageView sd_setImageWithURL:url placeholderImage:image options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) { 91 92 NSLog(@"下载进度:%f",(double)receivedSize / expectedSize); 93 94 } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { 95 NSLog(@"-----图片加载完毕-----%@",image); 96 }]; 97 98 return cell; 99 } 100 101 @end
1 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application 2 { 3 SDWebImageManager *maneger = [SDWebImageManager sharedManager]; 4 //1.取消正在下载的操作 5 [maneger cancelAll]; 6 //2.清除内存缓存 7 [maneger.imageCache clearMemory]; 8 maneger.imageCache.maxCacheAge = 100*24*60*60; 9 }