天天看点

iOS NSoperation

文章目录
    一、NSOperation简介
    二、NSOperation和NSOperationQueue的基本使用
        1、创建任务
        2、创建队列
        3、将任务加入到队列中
    三、控制串行执行和并行执行的关键
    四、操作依赖
    五、一些其他方法
           

一、NSoperation的简介

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

NSoperation 总共有两个子类 : NSInvocationOperation 和 NSBlockOperation。

NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。

因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:

1、创建任务:先将需要执行的操作封装到一个NSOperation对象中。

2、创建队列:创建NSOperationQueue对象。

3、将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中。

之后呢,系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。

注意和GCD 类比学习 : Operation 相当于任务、操作队列就和GCD其实是同个意思。总归来说,NSOperation 比GCD 更有自主性!例如: 可以通过继承重写main方法自定义任务、可暂停任务、可恢复任务、可通过KVO监Operation是否完成或取消,可设置任务权重优先执行任务、可设置等待等

下面我们来学习下NSOperation和NSOperationQueue的基本使用。

二、NSOperation和NSOperationQueue的基本使用

NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务。我们有三种方式来封装任务:

1、使用 NSInvocationOperation

2、使用 NSBlockOperation

3、自定义子类继承NSoperation,并重写main方法

1、在不使用NSOperationQueue,单独使用NSOperation的情况下系统同步执行操作,下面我们学习以下任务的三种创建方式。

1 ) 使用 NSInvocationOperation

NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];

  // 调用start 方法执行操作
 [operation start];


//其中run方法的定义如下
- (void)run
{
    NSLog(@"NSInvocationOperation -> 当前线程:%@", [NSThread currentThread]);   
}
           

运行结果 :

iOS NSoperation

说明 :从中可以看到,在没有使用队列(NSOperationQueue),单独使用NSInvocationOperation的情况下,NSInvocationOperation在主线程执行操作,并没有开启新线程。

.

.

.

2 ) 使用 NSBlockOperation

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation -> 当前线程:%@", [NSThread currentThread]);

    }]; 
    //  NSBlockOperation 提供 addExecutionBlock,可通过这个进行添加额外的操作,这些额外操作在有可能在主线程中执行,也有可能会开启子线程去执行。
    [blockOperation addExecutionBlock:^{

        NSLog(@"NSBlockOperation 额外操作任务1:%@", [NSThread currentThread]);

    }];

    [blockOperation addExecutionBlock:^{

        NSLog(@"NSBlockOperation 额外操作任务2:%@", [NSThread currentThread]);

    }];

    [blockOperation addExecutionBlock:^{

        NSLog(@"NSBlockOperation 额外操作任务3:%@", [NSThread currentThread]);

    }];

    [blockOperation start];
           

运行结果 :

iOS NSoperation

说明 :

<1> 在没有使用NSOperationQueue、单独使用NSBlockOperation的情况下,NSBlockOperation也是在主线程执行操作,并没有开启新线程

<2> NSBlockOperation 提供 addExecutionBlock,可通过这个进行添加额外的操作,这些额外操作在有可能在主线程中执行,也有可能会开启子线程去执行

.

.

.

3 ) 自定义子类继承NSoperation,并重写main方法

先定义一个继承自NSOperation的子类,重写main方法

CustomOperation.h

#import <Foundation/Foundation.h>

@interface CustomOperation : NSOperation

@end
           

CustomOperation.m

#import "CustomOperation.h"  
@implementation CustomOperation

- (void)main
{
    for (int i = ; i < ; i++) {

        NSLog(@"定义继承自NSOperation的子类封装任务:%@",[NSThread currentThread]);

    }

}

@end
           

导入头文件 CustomOperation.h

CustomOperation *customOperation = [[CustomOperation alloc]init];
 [customOperation start];
           

运行结果 :

iOS NSoperation

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

2、创建队列

和GCD中的并发队列、串行队列略有不同的是:NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。下边是主队列、其他队列的基本创建方法和特点。

 主队列

凡是添加到主队列的任务,必然是放在主线程中执行

 其它队列

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

3、将任务添加到队列中去

// 1、 创建 队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];

// 2、定义任务
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];


NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
         for (int i = ; i < ; i++) {
            NSLog(@"NSBlockOperation任务:%@",[NSThread currentThread]);


        }

    }];

// 3、 将任务添加到队列中
[queue addOperation:invocationOperation];
[queue addOperation:blockOperation];
           

运行结果 :

iOS NSoperation

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

当然,你也可以无需先创建任务,可以在block中添加任务,直接将block中的人添加到队列中。

[queue addOperationWithBlock:^{

        for (int i = ; i < ; i++) {

            NSLog(@"任务:%@",[NSThread currentThread]);

        }

    }];
           

运行结果 :

iOS NSoperation

.

.

.

三、控制串行执行和并行执行的关键

之前我们说过,NSOperationQueue创建的其他队列同时具有串行、并发功能,上边我们演示了并发功能,那么他的串行功能是如何实现的?

这里有个关键参数maxConcurrentOperationCount,这个官方的解释是如下

The maximum number of queued operations that can execute at the same time.
           

中文可译为 : 可以同时执行的队列操作的最大数量

 maxConcurrentOperationCount 默认值为-1,表示不进行限制,默认为并发执行

 当 maxConcurrentOperationCount = 1,串行执行任务

 当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。

注意 : 这个参数主要控制队列是串行执行任务还是并行执行任务,具体开启的线程数交由系统控制。

具体看下面一个例子 :

// 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

     //queue.maxConcurrentOperationCount = -1;
    queue.maxConcurrentOperationCount = ;

    // 添加操作
    [queue addOperationWithBlock:^{
        NSLog(@"1-----%@", [NSThread currentThread]);

    }];
    [queue addOperationWithBlock:^{
        NSLog(@"2-----%@", [NSThread currentThread]);

    }];
    [queue addOperationWithBlock:^{
        NSLog(@"3-----%@", [NSThread currentThread]);

    }];
           

当 maxConcurrentOperationCount = -1 时控制台输出

iOS NSoperation

当 maxConcurrentOperationCount = 1 时控制台输出

iOS NSoperation

可以看出:当最大并发数为1时,任务是按顺序串行执行的。当最大并发数为2时,任务是并发执行的。而且开启线程数量是由系统决定的,不需要我们来管理。这样看来,是不是比GCD还要简单了许多?

四、设置操作依赖

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

- (void)addOperationDepedency
{
    // 1、 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2、 创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1-----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2-----%@", [NSThread  currentThread]);
    }];


   //让op2 依赖于 op1,则先执行op1,在执行op2
    [op2 addDependency:op1];    

    // 3、 将任务添加到队列中
    [queue addOperation:op2];
    [queue addOperation:op1];


}//添加操作依赖
           

运行结果 :

iOS NSoperation

.

.

.

五、一些其他方法

 NSOperation提供的方法,可取消单个操作

- (void)cancel;

 NSOperationQueue提供的方法,可以取消队列的所有操作

- (void)cancelAllOperations;

 可设置任务的暂停和恢复,YES代表暂停队列,NO代表恢复队列

- (void)setSuspended:(BOOL)b;

 判断暂停状态

- (BOOL)isSuspended;

注意:
    这里的暂停和取消并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
    暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。
           

相关链接 :

本文Demo传送门

GCD和NSOperation的区别和选用