iOS基础 - 线程 1.2:NSOperation | NSOperationQueue


简言

1 - NSOperation 在 MVC 中属于 M,用来封装单个任务相关的代码和数据的抽象类。它是抽象的,是不能够直接使用,而是使用它的子类

2 - 使用 NSOperation ?类的方式有 3 种方式

① NSInvocationOperation:封装了执行操作的 target 和要执行的 action

② NSBlockOperation :封装了要执行的代码块

③ 自定义子类,实现内部相应?法

2 - NSOperation(含子类)只是一个操作,本身并无主、子线程之分,它可以调节它在队列中的优先级。它可在任意线程中使用,通常和 NSOperationQueue 搭配

3 - 操作队列:NSOperationQueue,用来管理一组 Operation 对象的执行。它会根据需要自动为 Operation 开辟合适数量的线程,以完成任务的并发执行

NSInvocationOperation 

1 - 如何使用 NSInvocationOperation

 1 - (void)viewDidLoad{
 2     [super viewDidLoad];
 3 
 4     // 创建操作对象,封装要执行的任务
 5     NSInvocationOperation *operation=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
 6     // 执行操作
 7     [operation start];
 8 
 9 }
10 
11 -(void)test{
12 
13     // 操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程
14     NSLog(@"--test--%@--",[NSThread currentThread]);
15     // --test--{number = 1, name = main}--
16     
17 }

NSBlockOperation

1 - 如何使用 NSBlockOperation

1     // 创建 NSBlockOperation 对象
2     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
3 
4         NSLog(@"%@",[NSThread currentThread]);
5         // {number = 1, name = main}
6     }];
7 
8     // 开启执行操作
9     [operation start];

2 - 只要 NSBlockOperation 封装的操作数 > 1,就会异步执行操作 

 1     NSBlockOperation *operation02 = [NSBlockOperation blockOperationWithBlock:^{
 2         
 3         NSLog(@"operation02------%@",[NSThread currentThread]);
 4     }];
 5 
 6     // 添加操作
 7     [operation02 addExecutionBlock:^{
 8         
 9         NSLog(@"add01------%@",[NSThread currentThread]);
10     }];
11 
12     [operation02 addExecutionBlock:^{
13         
14         NSLog(@"add02------%@",[NSThread currentThread]);
15     }];
16 
17     [operation02 addExecutionBlock:^{
18         
19         NSLog(@"add03------%@",[NSThread currentThread]);
20     }];
21     // 开启执行操作
22     [operation02 start];

日志打印

NSOperationQueue

1 - NSOperation 可以调? start ?法来执?任务,但默认是同步执行。如果将 NSOperation 添加到 NSOperationQueue 中,系统就会自动异步执行 NSOperation 中的操作

2 - 添加操作到 NSOperationQueue 中,自动开启线程

 1 - (void)viewDidLoad{
 2     [super viewDidLoad];
 3 
 4     // 创建对象,封装操作
 5     NSInvocationOperation *operation1=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
 6     NSInvocationOperation *operation2=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];
 7     
 8     NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
 9         
10         NSLog(@"operation3--1----%@",[NSThread currentThread]);
11     }];
12     
13     [operation3 addExecutionBlock:^{
14         
15         NSLog(@"operation3--2----%@",[NSThread currentThread]);
16     }];
17 
18     // 创建 NSOperationQueue 对象
19     NSOperationQueue * queue=[[NSOperationQueue alloc] init];
20     // 把操作添加到队列中
21     [queue addOperation:operation1];
22     [queue addOperation:operation2];
23     [queue addOperation:operation3];
24 }
25 
26 -(void)test1{
27     
28     NSLog(@"--test1--%@",[NSThread currentThread]);
29 }
30 
31 -(void)test2{
32     NSLog(@"--test2--%@",[NSThread currentThread]);
33 }

日志打印

说明:系统自动将 NSOperationqueue 中的 NSOperation 对象取出,将其封装的操作放到一条新的线程中执行

         上面的代码示例中,一共有四个任务:operation1 和 operation2 分别有一个任务,operation3 有两个任务,一共四个任务,开启四条线程。 

注:在将操作添加进队列前,禁止开启当前操作

1      // 利用队列开启子线程
2      NSInvocationOperation *operation01 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
3     
4      // 在未添加进队列前开启,默认主线程
5      [operation01 start];
6      // 继续创建队列,把 operation01 添加进去后...
7      NSOperationQueue * queue = [[NSOperationQueue alloc] init];
8      [queue setMaxConcurrentOperationCount:1];
9      [queue addOperation:operation01];// 运行崩溃。解决方案:屏蔽 [operation01 start]

NSOperation 常用 API

1 - 并发数:同时执?的任务数。最大并发数:同一时间最多只能执行的任务的个数 

- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

注:如果没有设置最大并发数,那么并发的个数是由系统内存和 CPU 决定的

      最大并发数建议不要乱写(5 以内),一般以 2~3 为宜

2 - 队列的取消,暂停和恢复

① 取消队列的所有操作(也可以调用 NSOperation 的 - (void)cancel ?法取消单个操作)

- (void)cancelAllOperations;

② 暂停和恢复队列

- (void)setSuspended:(BOOL)b; // YES 代表暂停队列,NO 代表恢复队列
- (BOOL)isSuspended; // 当前状态

说明:暂停和恢复的适用场合,如在 tableview 界面里开启线程下载网络,这会对 UI 会有所影响,使用户体验变差

         那么这种情况,就可以设置当用户操作 UI(如滚动屏幕)的时暂停队列(而不是取消队列),停止滚动的时候,恢复队列

3 - 操作优先级

① NSOperation 在 queue 中的优先级

  - (NSOperationQueuePriority)queuePriority; 2 - (void)setQueuePriority:(NSOperationQueuePriority)p;

② 优先级的取值:优先级高的任务,只能保证调用的几率会更大,而不是一定

1 NSOperationQueuePriorityVeryLow = -8L,
2 NSOperationQueuePriorityLow = -4L,
3 NSOperationQueuePriorityNormal = 0,
4 NSOperationQueuePriorityHigh = 4,
5 NSOperationQueuePriorityVeryHigh = 8 

4 - 操作依赖

① NSOperation 之间可以设置依赖来保证执行顺序,?如一定要让操作 A 执行完后,才能执行操作 B

1 [operationB addDependency:operationA]; // 操作 B 依赖于操作 A

② 可以在不同 queue 的 NSOperation 之间创建依赖关系

不能循环依赖(不能 A 依赖于 B 且 B 又依赖于 A)

③ 代码示例:设置依赖,使线程有序执行

 1 - (void)viewDidLoad
 2 {
 3     [super viewDidLoad];
 4 
 5     // 创建对象,封装操作
 6     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
 7     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];
 8     NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
 9         
10         for (int i=0; i<3; i++) {
11             NSLog(@"operation3---1---%@",[NSThread currentThread]);
12         }
13     }];
14     [operation3 addExecutionBlock:^{
15         
16         for (int i=0; i<3; i++) {
17             NSLog(@"operation3---2---%@",[NSThread currentThread]);
18         }
19     }];
20 
21     // 操作依赖:先执行 operation2,再执行 operation1,最后执行 operation3
22     [operation3 addDependency:operation1];
23     [operation1 addDependency:operation2];
24 
25 //    // 不能是相互依赖
26 //    [operation3 addDependency:operation1];
27 //    [operation1 addDependency:operation3];
28 
29     // 创建操作队列
30     NSOperationQueue * queue=[[NSOperationQueue alloc]init];
31     // 添加
32     [queue addOperation:operation1];
33     [queue addOperation:operation2];
34     [queue addOperation:operation3];
35 }
36 
37 -(void)test1{
38     
39     for (int i=0; i<3; i++) {
40         NSLog(@"NSInvocationOperation--test1--%@",[NSThread currentThread]);
41     }
42 }
43 
44 -(void)test2{
45     for (int i=0; i<3; i++) {
46         NSLog(@"NSInvocationOperation--test2--%@",[NSThread currentThread]);
47     }
48 }

日志打印

注:依赖一定要在操作添加进队列之前进行设置

  使用 Operation 的目的就是为了让开发人员不再关心线程

5 - 操作监听

① 可以监听一个操作的执行完毕

1 - (void (^)(void))completionBlock;
2 - (void)setCompletionBlock:(void (^)(void))block; 

② 代码示例:监听操作的执行状况

 1     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
 2         for (int i = 0; i<10; i++) {
 3             //..........
 4         }
 5     }];
 6 
 7     // 操作执行完毕后调用
 8     operation.completionBlock = ^{
 9         
10         //........
11     };
12 
13     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
14     [queue addOperation:operation];

相关