iOS多线程总结——NSOperation与NSOperationQueue的使用

Posted SSIrreplaceable

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS多线程总结——NSOperation与NSOperationQueue的使用相关的知识,希望对你有一定的参考价值。

本篇是多线程总结的第三篇,关于多线程的概念和NSThread的使用写在第一篇,《iOS多线程总结(1)——多线程相关概念及NSObject/NSThread的使用》,第二篇《iOS多线程总结(2)——GCD》主要讲解GCD的使用,本编是线程实现总结的最后一篇,主要讲解NSOperation的使用。

一. NSOperation

在MacOSXv10.6和ios4之前,NSOperation与NSOperationQueue不同于GCD,他们使用了完全不同的机制。从MacOSXv10.6和iOS4开始,NSOperation和NSOperationQueue是建立在GCD上的。NSOperation和NSOperationQueue对比GCD会带来一点额外的系统开销,但是你可以在多个操作Operation中添加附属。你可以重用操作,取消或者暂停他们。NSOperation和 Key-Value Observation (KVO)是兼容的,例如:你可以通过监听NSNotificationCenter去让一个操作开始执行。

NSOperation调用start方法即可开始执行操作,NSOperation对象默认按同步方式执行,也就是在调用start方法的那个线程中直接执行。NSOperation对象的isConcurrent方法会告诉我们这个操作相对于调用start方法的线程,是同步还是异步执行。isConcurrent方法默认返回NO,表示操作与调用线程同步执行。

NSOperation还可以和NSOperationQueue配合使用,开发人员不用调用start方法开始任务,也不用关注线程的什么时候开始什么时候结束,只需要静下心来研究当前的任务逻辑。

上面说的NSOperation使用主要指的是它的子类的使用,NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类才可以实现多线程,它的子类有:NSBlockOperationNSInvocationOperation自定义NSOperation的子类

(一). NSOperation的API

@interface NSOperation : NSObject 
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif


// 开始操作
- (void)start;

// 操作任务的入口,一般用于自定义NSOperation的子类 
- (void)main;

// 判断是否已经被取消
@property (readonly, getter=isCancelled) BOOL cancelled;

// 取消操作Operation,调用后不会自动马上取消,需要通过isCancelled方法检查是否被取消,然后自己编写代码退出当前的Operation
- (void)cancel;

// 是否正在执行
@property (readonly, getter=isExecuting) BOOL executing;

// 是否执行完
@property (readonly, getter=isFinished) BOOL finished;

// 判定该线程是否是并发线程,即调用该operation的start方法的线程是否与operation所在线程相同
// 注意:此属性即将被弃用,之后使用asynchronous属性代替
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below

// 是否异步执行
@property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0);

// 是否准备好
// 在start方法开始之前,需要确定Operation是否准备好,默认为YES,如果该operation没有准备好,则不会start。
@property (readonly, getter=isReady) BOOL ready;

// 添加依赖关系,如:[op1 addDependency:op2]; op2先执行,op1后执行  
- (void)addDependency:(NSOperation *)op;

// 取消依赖,注意:操作对象的依赖不能在操作队列执行时取消
- (void)removeDependency:(NSOperation *)op;

// 获取有依赖关系的Operation所组成的数组
@property (readonly, copy) NSArray<NSOperation *> *dependencies;

// Operation优先级的枚举
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) 
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
;

// 优先级
@property NSOperationQueuePriority queuePriority;

// Operation完成后调用的代码块
@property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);

// 是否等待Operation执行结束才执行后面的代码。如果为YES,会堵塞当前线程,直到Operation执行结束,才会执行接下来的代码
- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);

// 设定Operation的线程优先级,取值范围0~1,默认为0.5
// 即使设定了线程优先级,也只能保证其在该线程的main()方法范围内有效,Operation的其他代码仍然执行在默认线程
@property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0);

// 用于系统自动合理的管理队列的资源分配。提示:下文将进行介绍
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);

// 操作任务的名字
@property (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);

@end

(二). Quality of Service(QoS)

这是在iOS8之后提供的新功能,苹果提供了几个Quality of Service枚举来使用:user interactive, user initiated, utility 和 background。通过这些枚举告诉系统我们在进行什么样的工作,然后系统会通过合理的资源控制来最高效的执行任务代码,其中主要涉及到CPU调度的优先级、IO优先级、任务运行在哪个线程以及运行的顺序等等,我们可以通过一个抽象的Quality of Service枚举参数来表明任务的意图以及类别。

  • NSQualityOfServiceUserInteractive
    与用户交互的任务,这些任务通常跟UI级别的刷新相关,比如动画,这些任务需要在一瞬间完成.

  • NSQualityOfServiceUserInitiated
    由用户发起的并且需要立即得到结果的任务,比如滑动scroll view时去加载数据用于后续cell的显示,这些任务通常跟后续的用户交互相关,在几秒或者更短的时间内完成

  • NSQualityOfServiceUtility
    一些可能需要花点时间的任务,这些任务不需要马上返回结果,比如下载的任务,这些任务可能花费几秒或者几分钟的时间

  • NSQualityOfServiceBackground
    这些任务对用户不可见,比如后台进行备份的操作,这些任务可能需要较长的时间,几分钟甚至几个小时

  • NSQualityOfServiceDefault
    优先级介于user-initiated 和 utility,当没有 QoS信息时默认使用,开发者不应该使用这个值来设置自己的任务

二. NSBlockOperation

(一). 知识点

  1. 使用NSBlockOperation创建的Operation,如果是通过start方法启动操作,第一个操作不管是用blockOperationWithBlock:添加或者用addExecutionBlock:方法添加,第一个操作默认在主线程上执行;当添加的操作数大于1,其余的操作会开辟新线程执行操作。

  2. 如果通过completionBlock属性添加操作,系统会把completionBlock属性上的任务直接添加到系统开辟的最后一条线程上;也就是说,创建一个NSBlockOperation对象,然后往里面添加多个任务,当启动时,开辟多线程,completionBlock属性上的任务就会添加到开启的多个线程中的最后开启的那条线程上。

  3. 一旦Operation添加到操作队列queue中,queue就拥有了这个Operation对象并且不能被删除,唯一能做的事情是取消;通过cancel方法取消任务,Operation不会马上被取消,只能自己通过[op isCancelled]检测Operation是否被取消,然后编写退出操作的代码。

  4. 如果需要在当前线程中处理Operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待Operation完成,这个方法可以用于NSInvocationOperation对象,它有个属性result,用于获取操作的返回值。通常我们应该避免编写这样的代码,虽然阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用的主线程中等待一个Operation,只能在子线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

(二). NSBlockOperation的API

@interface NSBlockOperation : NSOperation 
@private
    id _private2;
    void *_reserved2;


// 添加操作任务
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;

// 添加操作任务
- (void)addExecutionBlock:(void (^)(void))block;

// 获取所有的操作任务所组成的数组
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;

@end

(三). NSBlockOperation的使用

1. 不使用NSOperationQueue

    // 使用blockOperationWithBlock:快速创建操作对象
//    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
//        
//        NSLog(@"---%@", [NSThread currentThread]);
//    ];

    // 使用传统的方法创建操作对象
    NSBlockOperation *op = [[NSBlockOperation alloc] init];

    // 创建一个NSOperation对象,然后往里面添加多个任务,当启动时,开辟多线程,
    // completionBlock属性上的任务就会添加到开启的多个线程中的最后开启的那条线程上
    op.completionBlock = ^

        NSLog(@"---%@", [NSThread currentThread]);
    ;

    // 添加操作
    // 如果是通过start方法启动操作,第一个操作不管是用blockOperationWithBlock:添加或者用addExecutionBlock:方法添加,
    // 第一个操作默认在主线程上执行,非第一个操作会开辟新线程执行操作
    [op addExecutionBlock:^

        NSLog(@"%@", [NSThread currentThread]);
    ];
    [op addExecutionBlock:^

        NSLog(@"%@", [NSThread currentThread]);
    ];
    [op addExecutionBlock:^

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

    // 开始操作任务
    [op start];

2. completionBloc属性与NSOperationQueue

    // 创建三个Operation对象
    NSBlockOperation *op1 = [[NSBlockOperation alloc] init];
    NSBlockOperation *op2 = [[NSBlockOperation alloc] init];
    NSBlockOperation *op3 = [[NSBlockOperation alloc] init];

    // 通过completionBlock属性添加操作
    op1.completionBlock = ^

        NSLog(@"---%@", [NSThread currentThread]);
    ;
    op2.completionBlock = ^

        NSLog(@"---%@", [NSThread currentThread]);
    ;
    op3.completionBlock = ^

        NSLog(@"---%@", [NSThread currentThread]);
    ;

    // 把Operation添加到操作队列中,会开辟多条线程并发执行completionBlock属性中得任务
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];

3. 使用NSOperationQueue

    // 创建三个Operation对象
    NSBlockOperation *op1 =[NSBlockOperation blockOperationWithBlock:^

        for (int i = 0; i < 10; i++) 
            NSLog(@"op----%d----%@", i, [NSThread currentThread]);
        
    ];
    NSBlockOperation *op2 =[NSBlockOperation blockOperationWithBlock:^

        for (int i = 0; i < 10; i++) 
            NSLog(@"op2----%d----%@", i, [NSThread currentThread]);
        
    ];
    NSBlockOperation *op3 =[NSBlockOperation blockOperationWithBlock:^

        for (int i = 0; i < 10; i++) 
            NSLog(@"op3----%d----%@", i, [NSThread currentThread]);
        
    ];

    // 给操作添加依赖,op2和op3并发执行,op1最后执行
    [op1 addDependency:op3];
    [op1 addDependency:op2];

    // 创建操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 把操作对象添加到操作队列中
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];

    NSLog(@"%@",[op1 dependencies]);

4. 取消Operation

#import "ViewController.h"

@interface ViewController ()

// 操作对象
@property (nonatomic, strong) NSBlockOperation *op;

@end

@implementation ViewController

- (void)viewDidLoad 
    [super viewDidLoad];

     _op = [NSBlockOperation blockOperationWithBlock:^

        for (int i = 0; i < 5000; i++) 

            // 判断是否被取消
            if ([_op isCancelled]) 
                NSLog(@"----op isCancelled----");
                break;

            

            NSLog(@"%d----%@", i, [NSThread currentThread]);
        

    ];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:_op];



- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 
    // 取消操作
    [_op cancel];

三. NSInvocationOperation

(一). NSInvocationOperation的API

@interface NSInvocationOperation : NSOperation 
@private
    id _inv;
    id _exception;
    void *_reserved2;


// 通过方法签名初始化Operation
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;

// 使用NSInvocation对象初始化Operation
// NSInvocation:调用类,创建的对象叫"调用对象"
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;

// 获取NSInvocation对象
@property (readonly, retain) NSInvocation *invocation;

// 获取操作的返回值
// 注意:方法的返回值必须是对象才能获取正确,不能是数值类型,且必须操作Operation完成才可以获取,
// 可以通过“依赖关系”或者“waitUntilFinished方法”保证操作结束,然后获取返回值
@property (nullable, readonly, retain) id result;

@end

(二). NSInvocationOperation的使用

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad 
    [super viewDidLoad];

    // NSInvocationOperation的基本使用
    [self InvocationOperation1];

    NSLog(@"-------------------打印结果分割线----------------------");

    // NSInvocationOperation与NSOperationQueue的配合使用
    [self InvocationOperation2];



#pragma mark - NSInvocationOperation的基本使用

- (void)InvocationOperation1 

    // 创建方式一
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    [op1 start];


    // 创建方式二

    // 根据“方法”来初始化“方法签名对象”
    // NSMethodSignature是“方法签名类”
    NSMethodSignature  *sig = [ViewController instanceMethodSignatureForSelector:@selector(run:two:)];

    // 根据“方法签名对象”来创建一个“调用对象”
    // NSInvocation是“调用类”
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];

    // 设置“调用对象”的“调用者”
    invocation.target = self;
    // 设置“调用对象”的“调用方法”
    invocation.selector = @selector(run:two:);

    // 设置“调用对象”的“调用方法”的参数
    // 这里的索引要从2开始,因为0和1已经被占据了,分别是:方法调用者和方法签名
    int value1 = 100;
    int value2 = 200;
    [invocation setArgument:&value1 atIndex:2];
    [invocation setArgument:&value2 atIndex:3];

    // 根据“调用对象”初始化Operation对象
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithInvocation:invocation];
    [op2 start];

    // 获取OP2的返回值
    // 注意:方法的返回值必须是对象才能获取正确,不能是数值类型,且必须操作Operation完成才可以获取
    NSLog(@"OP2的返回值:%@", op2.result );



- (void)run 

    NSLog(@"run ---- 线程%@", [NSThread currentThread]);


- (NSNumber *)run:(int)value1 two:(int)value2 

    NSLog(@"run:two: --- %d --- %d --- %@", value1, value2, [NSThread currentThread]);

    // 注意:返回必须是对象
    return @(value1 + value2);


#pragma mark - 配合NSOperationQueue使用

- (void)InvocationOperation2 

    // 1. 创建操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];


    // 2. 创建操作方法

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

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

    NSMethodSignature  *sig1 = [[self class] instanceMethodSignatureForSelector:@selector(run:two:)];
    NSInvocation *invocation1 = [NSInvocation invocationWithMethodSignature:sig1];
    invocation1.target = self;
    invocation1.selector = @selector(run:two:);
    int value1 = 100;
    int value2 = 200;
    [invocation1 setArgument:&value1 atIndex:2];
    [invocation1 setArgument:&value2 atIndex:3];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithInvocation:invocation1];

    NSMethodSignature  *sig2 = [[self class] instanceMethodSignatureForSelector:@selector(run:two:)];
    NSInvocation *invocation2 = [NSInvocation invocationWithMethodSignature:sig2];
    invocation2.target = self;
    invocation2.selector = @selector(run:two:);
    int value3 = 400;
    int value4 = 550;
    [invocation2 setArgument:&value3 atIndex:2];
    [invocation2 setArgument:&value4 atIndex:3];
    NSInvocationOperation *op4 = [[NSInvocationOperation alloc] initWithInvocation:invocation2];


    // 3. 添加操作任务
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];



@end

输出结果:

四. 自定义NSOperation子类

如果NSInvocationOperation和NSBlockOperation对象不能满足需求,可以直接继承NSOperation,并添加任何你想要的行为。继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation。定义非并发的NSOperation要简单许多,只需要重载-(void)main这个方法,在这个方法里面执行主任务,并正确地响应取消事件,即在main()函数中明确地检查,当isCancelled==YES时操作被取消掉。

如果需要创建一个并发的Operation,并手动执行,则需要重写main,start,isCancelled,isReady,isConcurrent,isFinished,isExcluting,dependency,queuepripority方法,并维护每个property的KVO,同时确保每个property的原子性,比较繁琐,这里暂时不介绍。

非并发的实现步骤:

  • 继承NSOperation类
  • 重写main()方法
  • 在main()方法中创建一个@autoreleasepool
  • 将要做的操作放在@autoreleasepool中
    提示:创建自动释放池的原因:因为在异步执行情况下,不能访问主线程的自动释放池,所以应该自己创建一个自动释放池。

非并发的实现代码:

---------- 自定义Operation

#import "SJMSerialOperation.h"

@implementation SJMSerialOperation

- (void)main 
    // 自动释放池
    @autoreleasepool 

        for (int i = 0; i < 100; i++) 
            NSLog(@"%i --- %@", i, [NSThread currentThread]);
        

        // 被取消时退出
        if (self.isCancelled == YES) return;

        for (int i = 0; i < 100; i++) 
            NSLog(@"%i --- %@", i, [NSThread currentThread]);
        

        // 被取消时退出
        if (self.isCancelled == YES) return;

    


@end


---------- 自定义Operation的使用


#import "ViewController.h"
#import "SJMSerialOperation.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad 
    [super viewDidLoad];

    // 创建Operation对象    
    SJMSerialOperation *op = [[SJMSerialOperation alloc] init];
    // 执行,op会在主线程中执行
    [op start];


    // 创建两个Operation对象
    SJMSerialOperation *op1 = [[SJMSerialOperation alloc] init];
    SJMSerialOperation *op2 = [[SJMSerialOperation alloc] init];

    // 创建操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 把Operation对象添加到操作队列
    [queue addOperation:op1];
    [queue addOperation:op2];  


@end

五. NSOperationQueue

(一). 知识点

  1. NSOperationQueue称为操作队列,它的作用是管理子线程的生命周期和线程同步问题,有了操作队列,我们只需要关注具体的操作任务,而不用去管线程的生命周期和线程同步问题等问题,大大提高了开发效率。

  2. 只要在操作队列中加入操作任务,程序运行时就会去检查maxConcurrentOperationCount(最大并发数属性),根据最大并发数的值开辟线程。如果值是1,它就相当于串行队列,只开辟一条线程,如果大于1,就根据值开辟相应条数的子线程,当任务操作完后回自动销毁线程。

  3. 当通过设置suspended属性来暂停/挂起操作队列时,队列不会马上暂停,系统会执行完当前正在执行的任务,暂停下一个将要执行的任务。

  4. 一旦Operation对象添加到操作队列就不能被删除,只能通过Operation对象方法cancel取消;如果要取消全部操作队列中得任务,可以使用cancelAllOperations方法;但要成功取消,必须自己编写监听代码和取消代码,使用NSOperation对象的isCancelled方法在任务中检查是否被取消,如果被取消了,可以通过return等方法退出任务。

(二). NSOperationQueue的API

// 默认最大操作数为-1
static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;

@interface NSOperationQueue : NSObject 
@private
    id _private;
    void *_reserved;


// 添加操作对象(NSOperation对象)
- (void)addOperation:(NSOperation *)op;

// 添加操作对象组(NSOperation对象),waitUntilFinished:是否阻塞当前线程,等待所有操作都完成
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);

// 添加操作任务
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

// 获取操作任务对象组
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;

// 获取操作任务对象总数
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);

// 最大并发数
@property NSInteger maxConcurrentOperationCount;

// 是否暂停队列
@property (getter=isSuspended) BOOL suspended;

// 队列名字
@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);

// 用于系统自动合理的管理队列的资源分配
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);

// underlyingQueue属性的值是主线程的调度队列,此属性不能设置为其它值
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);

// 取消所有操作任务,配合NSOperation对象的isCancelled方法使用
- (void)cancelAllOperations;

// 阻塞当前线程,等待所有操作执行完毕 
- (void)waitUntilAllOperationsAreFinished;

// 获取当前操作队列
+ (nullable NSOperationQueue *)currentQueue NS_AVAILABLE(10_6, 4_0);

// 获取主操作队列
+ (NSOperationQueue *)mainQueue NS_AVAILABLE(10_6, 4_0);

@end

(三). 使用方法

1. NSOperationQueue单独使用

NSOperationQueue可以单独使用,不用自己创建NSOperation对象,可以直接通过addOperationWithBlock:方法往操作队列添加一个任务,它的底层会自动创建一个NSBlockOperation对象,再通过addOperation:方法把NSBlockOperation对象加入到操作队列中。

代码举例:

  • 继承NSOperationQueue,创建一个子类SJMOperationQueue,重写addOperationWithBlock:和addOperation:方法
#import "SJMOperationQueue.h"

@implementation SJMOperationQueue

-(void)addOperationWithBlock:(void (^)(void))block 

    NSLog(@"%s",__func__);

    [super addOperationWithBlock:block];


- (void)addOperation:(NSOperation *)op 

    NSLog(@"%s",__func__);

    [super addOperation:op];


@end
  • 使用addOperationWithBlock:方法直接添加任务
#import "ViewController.h"
#include "SJMOperationQueue.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 

    [self operationQueue];


- (void)operationQueue 

    SJMOperationQueue *queue = [[SJMOperationQueue alloc] init];

    [queue addOperationWithBlock:^

        NSLog(@"-----%@",[NSThread currentThread]);

    ];
    [queue addOperationWithBlock:^

        NSLog(@"+++++%@",[NSThread currentThread]);

    ];

    NSLog(@"%@",queue.operations);


@end

打印结果:

2. NSOperationQueue配合NSOperation的子类使用

  • 自定义NSOperation的子类
#import "SJMNSOperation.h"

@implementation SJMNSOperation

- (void)main 
    @autoreleasepool 
       for (int i = 0; i < 1000; i++) 
          NSLog(@"op3---1---%@", [NSThread currentThread]);
        
      


@end
  • NSOperationQueue配合NSOperation的子类使用
#import "ViewController.h"
#include "SJMNSOperation.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad 

    // 1. 创建操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2. 添加操作任务

    // 2.1 通过NSInvocationOperation创建操作任务
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

    // 2.2 通过NSBlockOperation创建操作任务
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^
        NSLog(@"%@",[NSThread currentThread]);
    ];

    // 2.3 通过自定义的NSOperation子类创建操作任务
    SJMNSOperation *op3 = [[SJMNSOperation alloc] init];

    // 3. 添加到操作队列
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];


- (void)run 

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

3. 取消队列中的所有操作

#import "ViewController.h"
#include "SJMOperationQueue.h"
#include "SJMNSOperation.h"

@interface ViewController ()

@property (nonatomic, strong) NSOperationQueue *queue;

@end

@implementation ViewController

- (void)viewDidLoad 
    [super viewDidLoad];

    // 创建操作队列
    _queue = [[NSOperationQueue alloc] init];

    // 添加操作任务
    [self operationQueue2];


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 

    // 取消所有操作任务
    [_queue cancelAllOperations];  


#pragma mark - 取消操作队列里所有的任务

- (void)operationQueue2 

    // 通过自定义的NSOperation子类创建操作任务
    SJMNSOperation *op1 = [[SJMNSOperation alloc] init];
    SJMNSOperation *op2 = [[SJMNSOperation alloc] init];
    SJMNSOperation *op3 = [[SJMNSOperation alloc] init];
    SJMNSOperation *op4 = [[SJMNSOperation alloc] init];
    SJMNSOperation *op5 = [[SJMNSOperation alloc] init];

    // 添加到操作队列
    [_queue addOperation:op1];
    [_queue addOperation:op2];
    [_queue addOperation:op3];
    [_queue addOperation:op4];
    [_queue addOperation:op5];

以上是关于iOS多线程总结——NSOperation与NSOperationQueue的使用的主要内容,如果未能解决你的问题,请参考以下文章

iOS多线程编程——GCD与NSOperation总结

iOS开发NSOperation 三:操作依赖和监听以及线程间通信

Ios 多线程之NSOperation与NSOprationQueue

iOS并发编程对比总结,NSThread,NSOperation,GCD - iOS

Ios 多线程之NSOperation与NSOprationQueue

iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用