1 简介

NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。

NSoperation 总共有两个子类 : NSInvocationOperation 和 NSBlockOperation
NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。因为NSoperation是基于GCD的,所以NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:

2 NSOperation和NSOperationQueue的基本使用

NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务。我们有三种方式来封装任务:
1、使用 NSInvocationOperation
2、使用 NSBlockOperation
3、自定义子类继承NSoperation,并重写main方法

  1. //NSOperation
  2. NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
  3. [operation start];
  4. - (void)run {
  5. NSLog(@"NSInvocationOperation -> 当前线程:%@", [NSThread currentThread]);
  6. }

运行结果为:
image.png
从上述我们可以看到NSInvocationOperation单独使用时,是在主线程中进行的,并没有开启子线程.

  1. NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
  2. NSLog(@"NSInvocationOperation -> 当前线程:%@", [NSThread currentThread]);
  3. }];
  4. [block addExecutionBlock:^{
  5. NSLog(@"其他操作");
  6. }];

NSBlockOperation同理,单独使用也是不会在开辟线程的.NSBlockOperation提供 addExecutionBlock,可通过这个进行添加额外的操作,这些额外操作在有可能在主线程中执行,也有可能会开启子线程去执行

  1. #import "CustomOperation.h"
  2. @implementation CustomOperation
  3. -(void)main {
  4. for (int i = 0; i <3; i ++) {
  5. NSLog(@"当前线程:%@", [NSThread currentThread]);
  6. }
  7. }
  8. @end
  1. CustomOperation *customOperation = [[CustomOperation alloc]init];
  2. [customOperation start];

打印结果

image.png
在没有使用NSOperationQueue、单独使用自定义子类的情况下,是在主线程执行操作,并没有开启新线程。

3 创建队列

和GCD中的并发队列、串行队列略有不同的是:NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。
主队列:凡是添加到主队列的任务,必然是放在主线程中执行.

  1. NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

其他队列:添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行,同时包含了:串行、并发功能.

  1. NSOperationQueue *queue = [[NSOperationQueue alloc]init];

讲任务添加到队列中去.

  1. // 1、 创建 队列
  2. NSOperationQueue *queue = [[NSOperationQueue alloc]init];
  3. // 2、定义任务
  4. NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
  5. NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
  6. for (int i = 0; i < 2; i++) {
  7. NSLog(@"NSBlockOperation任务:%@",[NSThread currentThread]);
  8. }
  9. }];
  10. // 3、 将任务添加到队列中
  11. [queue addOperation:invocationOperation];

打印结果.
image.png
说明 :NSInvocationOperation和NSOperationQueue结合后能够开启新线程,进行并发执行NSBlockOperation和NSOperationQueue也能够开启新线程,进行并发执行。

4 控制串行和并行maxConcurrentOperationCount

当maxConcurrentOperationCount 默认值为-1,表示不进行限制,默认为并发执行
当 maxConcurrentOperationCount = 1,串行执行任务
当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。
注意 : 这个参数主要控制队列是串行执行任务还是并行执行任务,具体开启的线程数交由系统控制。

  1. NSOperationQueue *queue = [[NSOperationQueue alloc]init];
  2. queue.maxConcurrentOperationCount = -1;
  3. // queue.maxConcurrentOperationCount = 1;
  4. [queue addOperationWithBlock:^{
  5. NSLog(@"1-----%@", [NSThread currentThread]);
  6. }];
  7. [queue addOperationWithBlock:^{
  8. NSLog(@"2-----%@", [NSThread currentThread]);
  9. }];
  10. [queue addOperationWithBlock:^{
  11. NSLog(@"3-----%@", [NSThread currentThread]);
  12. }];

当maxConcurrentOperationCount为-1时,打印结果
image.png
当maxConcurrentOperationCount为1时,打印结果
image.png

5 设置操作依赖

NSOperation和NSOperationQueue最吸引人的地方是它能添加操作之间的依赖关系。比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A。具体如下:

  1. // 1、 创建队列
  2. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  3. // 2、 创建任务
  4. NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
  5. NSLog(@"1-----%@", [NSThread currentThread]);
  6. }];
  7. NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
  8. NSLog(@"2-----%@", [NSThread currentThread]);
  9. }];
  10. //让op2 依赖于 op1,则先执行op1,在执行op2
  11. [op2 addDependency:op1];
  12. // 3、 将任务添加到队列中
  13. [queue addOperation:op2];
  14. [queue addOperation:op1];

打印结果:
image.png

6 GCD和NSOperation的区别

  • GCD是底层的C语言构成的API,而NSOperationQueue及相关对象是Objc的对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构;而Operation作为一个对象,为我们提供了更多的选择;
  • 在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的block(其实是有的,但需要许多复杂的代码);
  • NSOperation能够方便地设置依赖关系,我们可以让一个Operation依赖于另一个Operation,这样的话尽管两个Operation处于同一个并行队列中,但前者会直到后者执行完毕后再执行;
  • 我们能将KVO应用在NSOperation中,可以监听一个Operation是否完成或取消,这样子能比GCD更加有效地掌控我们执行的后台任务;
  • 在NSOperation中,我们能够设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行,而在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务的优先级,也需要大量的复杂代码;
  • 我们能够对NSOperation进行继承,在这之上添加成员变量与成员方法,提高整个代码的复用度,这比简单地将block任务排入执行队列更有自由度,能够在其之上添加更多自定制的功能。