NSOperation基本操作
一、并发数
(1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3
(2)最大并发数:同一时间最多只能执行的任务的个数。
(3)最⼤大并发数的相关⽅方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
说明:如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点。
注意:num的值并不代表线程的个数,仅仅代表线程的ID。
提示:最大并发数不要乱写(5以内),不要开太多,一般以2~3为宜,因为虽然任务是在子线程进行处理的,但是cpu处理这些过多的子线程可能会影响UI,让UI变卡。
二、队列的取消,暂停和恢复
(1)取消队列的所有操作
- (void)cancelAllOperations;
提⽰:也可以调用NSOperation的- (void)cancel⽅法取消单个操作
(2)暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended; //当前状态
(3)暂停和恢复的适用场合:在tableview界面,开线程下载远程的网络界面,对UI会有影响,使用户体验变差。那么这种情况,就可以设置在用户操作UI(如滚动屏幕)的时候,暂停队列(不是取消队列),停止滚动的时候,恢复队列。
三、操作优先级
(1)设置NSOperation在queue中的优先级,可以改变操作的执⾏优先级
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
(2)优先级的取值
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
说明:优先级高的任务,调用的几率会更大。
四、操作依赖
(1)NSOperation之间可以设置依赖来保证执行顺序,⽐如一定要让操作A执行完后,才能执行操作B,可以像下面这么写
[operationB addDependency:operationA]; // 操作B依赖于操作
(2)可以在不同queue的NSOperation之间创建依赖关系
注意:不能循环依赖(不能A依赖于B,B又依赖于A)。
(3)代码示例
1 #import "YYViewController.h"
2
3 @interface YYViewController ()
4
5 @end
6
7 @implementation YYViewController
8
9 - (void)viewDidLoad
10 {
11 [super viewDidLoad];
12
13 //创建NSInvocationOperation对象,封装操作
14 NSInvocationOperation *operation1=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test1) object:nil];
15 NSInvocationOperation *operation2=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test2) object:nil];
16 //创建对象,封装操作
17 NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^{
18 for (int i=0; i<5; i++) {
19 NSLog(@"NSBlockOperation3--1----%@",[NSThread currentThread]);
20 }
21 }];
22 [operation3 addExecutionBlock:^{
23 for (int i=0; i<5; i++) {
24 NSLog(@"NSBlockOperation3--2----%@",[NSThread currentThread]);
25 }
26 }];
27
28 //设置操作依赖
29 //先执行operation2,再执行operation1,最后执行operation3
30 [operation3 addDependency:operation1];
31 [operation1 addDependency:operation2];
32
33 //不能是相互依赖
34 // [operation3 addDependency:operation1];
35 // [operation1 addDependency:operation3];
36
37 //创建NSOperationQueue
38 NSOperationQueue * queue=[[NSOperationQueue alloc]init];
39 //把操作添加到队列中
40 [queue addOperation:operation1];
41 [queue addOperation:operation2];
42 [queue addOperation:operation3];
43 }
44
45 -(void)test1
46 {
47 for (int i=0; i<5; i++) {
48 NSLog(@"NSInvocationOperation--test1--%@",[NSThread currentThread]);
49 }
50 }
51
52 -(void)test2
53 {
54 for (int i=0; i<5; i++) {
55 NSLog(@"NSInvocationOperation--test2--%@",[NSThread currentThread]);
56 }
57 }
58
59 @end
打印查看:
A做完再做B,B做完才做C。
注意:一定要在添加之前,进行设置。
提示:任务添加的顺序并不能够决定执行顺序,执行的顺序取决于依赖。使用Operation的目的就是为了让开发人员不再关心线程。
5.操作的监听
可以监听一个操作的执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
代码示例
第一种方式:可以直接跟在任务后面编写需要完成的操作,如这里在下载图片后,紧跟着下载第二张图片。但是这种写法有的时候把两个不相关的操作写到了一个代码块中,代码的可阅读性不强。
1 #import "YYViewController.h"
2
3 @interface YYViewController ()
4
5 @end
6
7 @implementation YYViewController
8
9 - (void)viewDidLoad
10 {
11 [super viewDidLoad];
12
13 //创建对象,封装操作
14 NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
15 NSLog(@"-operation-下载图片-%@",[NSThread currentThread]);
16 //.....下载图片后继续进行的操作
17 NSLog(@"--接着下载第二张图片--");
18 }];
19
20 //创建队列
21 NSOperationQueue *queue=[[NSOperationQueue alloc]init];
22 //把任务添加到队列中(自动执行,自动开线程)
23 [queue addOperation:operation];
24 }
25
26 @end
第二种方式:
1 #import "YYViewController.h"
2
3 @interface YYViewController ()
4
5 @end
6
7 @implementation YYViewController
8
9 - (void)viewDidLoad
10 {
11 [super viewDidLoad];
12
13 //创建对象,封装操作
14 NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
15 for (int i=0; i<10; i++) {
16 NSLog(@"-operation-下载图片-%@",[NSThread currentThread]);
17 }
18 }];
19
20 //监听操作的执行完毕
21 operation.completionBlock=^{
22 //.....下载图片后继续进行的操作
23 NSLog(@"--接着下载第二张图片--");
24 };
25
26 //创建队列
27 NSOperationQueue *queue=[[NSOperationQueue alloc]init];
28 //把任务添加到队列中(自动执行,自动开线程)
29 [queue addOperation:operation];
30 }
31
32 @end
打印查看:
说明:在上一个任务执行完后,会执行operation.completionBlock=^{}代码段,且是在当前线程执行(2)。
自定义NSOperation
一、实现一个简单的tableView显示效果
实现效果展示:
代码示例(使用以前在主控制器中进行业务处理的方式)
1.新建一个项目,让控制器继承自UITableViewController。
1 //
2 // YYViewController.h
3 // 01-自定义Operation
4 //
5 // Created by apple on 15-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import
10
11 @interface YYViewController : UITableViewController
12
13 @end
2.处理storyboard中得界面,如下:
3.根据plist文件,字典转模型
新建一个类,继承自NSObject,作为数据的模型
YYappModel.h文件
1 //
2 // YYappModel.h
3 // 01-自定义Operation
4 //
5 // Created by apple on 14-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import
10
11 @interface YYappModel : NSObject
12 /**
13 *应用名称
14 */
15 @property(nonatomic,copy)NSString *name;
16 /**
17 * 应用图片
18 */
19 @property(nonatomic,copy)NSString *icon;
20 /**
21 * 应用的下载量
22 */
23 @property(nonatomic,copy)NSString *download;
24
25 +(instancetype)appModelWithDict:(NSDictionary *)dict;
26 -(instancetype)initWithDict:(NSDictionary *)dict;
27 @end
YYappModel.m文件
1 //
2 // YYappModel.m
3 // 01-自定义Operation
4 //
5 // Created by apple on 15-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import "YYappModel.h"
10
11 @implementation YYappModel
12
13 -(instancetype)initWithDict:(NSDictionary *)dict
14 {
15 if (self=[super init]) {
16 [self setValuesForKeysWithDictionary:dict];
17 }
18 return self;
19 }
20
21 //工厂方法
22 +(instancetype)appModelWithDict:(NSDictionary *)dict
23 {
24 return [[self alloc]initWithDict:dict];
25 }
26 @end
主控制器中得逻辑控制部分,YYViewController.m文件
1 //
2 // YYViewController.m
3 // 01-自定义Operation
4 //
5 // Created by apple on 14-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import "YYViewController.h"
10 #import "YYappModel.h"
11
12 @interface YYViewController ()
13 @property(nonatomic,strong)NSArray *apps;
14
15 @end
16
17 @implementation YYViewController
18 #pragma mark- 懒加载
19 -(NSArray *)apps
20 {
21 if (_apps==nil) {
22 NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
23 NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
24
25 //字典转模型
26 NSMutableArray *array=[NSMutableArray array];
27 for (NSDictionary *dict in tempArray) {
28 YYappModel *app=[YYappModel appModelWithDict:dict];
29 [array addObject:app];
30 }
31 _apps=array;
32 }
33 return _apps;
34 }
35
36 #pragma mark-数据源方法
37 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
38 {
39 return self.apps.count;
40 }
41
42 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
43 {
44 static NSString *ID=@"ID";
45 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
46 if (cell==nil) {
47 cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
48 }
49 YYappModel *app=self.apps[indexPath.row];
50 cell.textLabel.text=app.name;
51 cell.detailTextLabel.text=app.download;
52
53 //下载图片数据
54 NSLog(@"加载图片数据---%@", [NSThread currentThread]);
55 NSURL *url=[NSURL URLWithString:app.icon];
56 NSData *data=[NSData dataWithContentsOfURL:url];
57 UIImage *imgae=[UIImage imageWithData:data];
58 cell.imageView.image=imgae;
59 NSLog(@"完成显示");
60 return cell;
61 }
62
63 @end
打印查看:
二、自定义NSOperation
说明:上面的下载图片数据部分是一个非常耗时的操作,这个操作任务在主线程完成,会严重的影响到用户体验,造成UI卡的现象。下面通过自定义NSOperation,新开线程,让加载图片的任务异步执行。
1.通过代理
在上面的基础上,新建一个类,让其继承自NSOperation。
YYdownLoadOperation.h文件
1 //
2 // YYdownLoadOperation.h
3 // 01-自定义Operation
4 //
5 // Created by apple on 15-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import
10
11 #pragma mark-设置代理和代理方法
12 @class YYdownLoadOperation;
13 @protocol YYdownLoadOperationDelegate
14 -(void)downLoadOperation:(YYdownLoadOperation*)operation didFishedDownLoad:(UIImage *)image;
15 @end
16
17 @interface YYdownLoadOperation : NSOperation
18 @property(nonatomic,copy)NSString *url;
19 @property(nonatomic,strong)NSIndexPath *indexPath;
20 @property(nonatomic,strong)id delegate;
21 @end
YYdownLoadOperation.m文件
1 //
2 // YYdownLoadOperation.m
3 // 01-自定义Operation
4 //
5 // Created by apple on 14-6-26.
6 // Copyright (c) 2014年 itcase. All rights reserved.
7 //
8
9 #import "YYdownLoadOperation.h"
10
11 @implementation YYdownLoadOperation
12 -(void)main
13 {
14 NSURL *url=[NSURL URLWithString:self.url];
15 NSData *data=[NSData dataWithContentsOfURL:url];
16 UIImage *imgae=[UIImage imageWithData:data];
17
18 NSLog(@"--%@--",[NSThread currentThread]);
19 //图片下载完毕后,通知代理
20 if ([self.delegate respondsToSelector:@selector(downLoadOperation:didFishedDownLoad:)]) {
21 dispatch_async(dispatch_get_main_queue(), ^{//回到主线程,传递数据给代理对象
22 [self.delegate downLoadOperation:self didFishedDownLoad:imgae];
23 });
24 }
25 }
26 @end
主控制器中的业务逻辑:
1 //
2 // YYViewController.m
3 // 01-自定义Operation
4 //
5 // Created by apple on 15-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import "YYViewController.h"
10 #import "YYappModel.h"
11 #import "YYdownLoadOperation.h"
12
13 @interface YYViewController ()
14 @property(nonatomic,strong)NSArray *apps;
15 @property(nonatomic,strong)NSOperationQueue *queue;
16
17 @end
18
19 @implementation YYViewController
20 #pragma mark- 懒加载apps
21 -(NSArray *)apps
22 {
23 if (_apps==nil) {
24 NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
25 NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
26
27 //字典转模型
28 NSMutableArray *array=[NSMutableArray array];
29 for (NSDictionary *dict in tempArray) {
30 YYappModel *app=[YYappModel appModelWithDict:dict];
31 [array addObject:app];
32 }
33 _apps=array;
34 }
35 return _apps;
36 }
37
38 #pragma mark-懒加载queue
39 -(NSOperationQueue *)queue
40 {
41 if (_queue==Nil) {
42 _queue=[[NSOperationQueue alloc]init];
43 //设置最大并发数为3
44 _queue.maxConcurrentOperationCount=3;
45 }
46 return _queue;
47 }
48
49 #pragma mark-数据源方法
50 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
51 {
52 return self.apps.count;
53 }
54 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
55 {
56 static NSString *ID=@"ID";
57 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
58 if (cell==nil) {
59 cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
60 }
61 YYappModel *app=self.apps[indexPath.row];
62 cell.textLabel.text=app.name;
63 cell.detailTextLabel.text=app.download;
64
65 //下载图片数据
66 // NSLog(@"加载图片数据---%@", [NSThread currentThread]);
67 // NSURL *url=[NSURL URLWithString:app.icon];
68 // NSData *data=[NSData dataWithContentsOfURL:url];
69 // UIImage *imgae=[UIImage imageWithData:data];
70 // cell.imageView.image=imgae;
71
72 //创建一个OPeration对象
73 YYdownLoadOperation *operation=[[YYdownLoadOperation alloc]init];
74 operation.url=app.icon;
75 operation.indexPath=indexPath;
76 operation.delegate=self;
77
78 //把操作对象添加到队列中在去
79 [self.queue addOperation:operation];
80
81 // NSLog(@"完成显示");
82 return cell;
83 }
84 -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image
85 {
86 //返回图片数据给每行对应的cell的imageview.image
87 //取出tableview中indexPath这一行对应的cell
88 UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:operation.indexPath];
89 //显示图片
90 cell.imageView.image=image;
91 // NSLog(@"cell--index--%@---%@",operation.indexPath,[NSThread currentThread]);
92 //一定要刷新表格
93 [self.tableView reloadData];
94 NSLog(@"--%@--",[NSThread currentThread]);
95
96 }
97 @end
说明:通过打印可以发现上面的代码存在很大的问题。
问题1:需要保证一个url对应一个operation对象。
问题2:下载完需要移除。移除执行完毕的操作。
问题3:保证一个url对应一个image。
下面对主控制器中得代码进行改进:
1 //
2 // YYViewController.m
3 // 01-自定义Operation
4 //
5 // Created by apple on 15-7-29.
6 // Copyright (c) 2015年 itcase. All rights reserved.
7 //
8
9 #import "YYViewController.h"
10 #import "YYappModel.h"
11 #import "YYdownLoadOperation.h"
12
13 @interface YYViewController ()
14 @property(nonatomic,strong)NSArray *apps;
15 @property(nonatomic,strong)NSOperationQueue *queue;
16 @property(nonatomic,strong)NSMutableDictionary *operations;
17 @property(nonatomic,strong)NSMutableDictionary *images;
18
19 @end
20
21 @implementation YYViewController
22 #pragma mark- 懒加载apps
23 -(NSArray *)apps
24 {
25 if (_apps==nil) {
26 NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
27 NSArray *tempArray=[NSArray arrayWithContentsOfFile:path];
28
29 //字典转模型
30 NSMutableArray *array=[NSMutableArray array];
31 for (NSDictionary *dict in tempArray) {
32 YYappModel *app=[YYappModel appModelWithDict:dict];
33 [array addObject:app];
34 }
35 _apps=array;
36 }
37 return _apps;
38 }
39
40 #pragma mark-懒加载queue
41 -(NSOperationQueue *)queue
42 {
43 if (_queue==Nil) {
44 _queue=[[NSOperationQueue alloc]init];
45 //设置最大并发数为3
46 _queue.maxConcurrentOperationCount=3;
47 }
48 return _queue;
49 }
50
51 #pragma mark-懒加载operations
52 -(NSMutableDictionary *)operations
53 {
54 if (_operations==Nil) {
55 _operations=[NSMutableDictionary dictionary];
56 }
57 return _operations;
58 }
59
60 #pragma mark-懒加载images
61 -(NSMutableDictionary *)images
62 {
63 if (_images==Nil) {
64 _images=[NSMutableDictionary dictionary];
65 }
66 return _images;
67 }
68
69 #pragma mark-数据源方法
70 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
71 {
72 return self.apps.count;
73 }
74 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
75 {
76 static NSString *ID=@"ID";
77 UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
78 if (cell==nil) {
79 cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
80 }
81 YYappModel *app=self.apps[indexPath.row];
82 cell.textLabel.text=app.name;
83 cell.detailTextLabel.text=app.download;
84
85 //保证一个url对应一个image对象
86 UIImage *image=self.images[app.icon];
87 if (image) {//缓存中有图片
88 cell.imageView.image=image;
89 }else // 缓存中没有图片,得下载
90 {
91 //先设置一张占位图片
92 cell.imageView.image=[UIImage imageNamed:@"57437179_42489b0"];
93 YYdownLoadOperation *operation=self.operations[app.icon];
94 if (operation) {//正在下载
95 //什么都不做
96 }else //当前没有下载,那就创建操作
97 {
98 operation=[[YYdownLoadOperation alloc]init];
99 operation.url=app.icon;
100 operation.indexPath=indexPath;
101 operation.delegate=self;
102 [self.queue addOperation:operation];//异步下载
103 self.operations[app.icon]=operation;
104 }
105 }
106
107
108 return cell;
109 }
110 -(void)downLoadOperation:(YYdownLoadOperation *)operation didFishedDownLoad:(UIImage *)image
111 {
112 //1.移除执行完毕的操作
113 [self.operations removeObjectForKey:operation.url];
114
115 //2.将图片放到缓存中
116 self.images[operation.url]=image;
117
118 //3.刷新表格(只刷新下载的那一行)
119
120 [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
121 NSLog(@"--%d--%@--",operation.indexPath.row,[NSThread currentThread]);
122
123 }
124 @end
打印查看: