IOS-多线程(NSOperation)

 一、基础用法

  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 }
原文地址:https://www.cnblogs.com/oc-bowen/p/5144991.html