block与GCD--43:NSOperation 与NSOperationQueue

2023-11-22 08:31:20 浏览数 (1)

NSOperation

  • NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
  • NSOperation的子类
    • NSInvocationOperation
    • NSBlockOperation
    • 自定义子类继承NSOperation,实现内部相应的方法

关于NSInvocationOperation

  • 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
  • 只有NSInvocationOperation放到一个NSOperationQueue中,才会异步执行
代码语言:javascript复制
- (IBAction)invocationOperation:(id)sender {
    //初始化Operation子类
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
    //调用start方法开始执行操作,一旦执行操作就会调用run方法
    [operation start];
}
-(void)run{
    NSLog(@"0--%@",[NSThread currentThread]);
}

invocationOperation.png

关于NSBlockOperation

  • 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
  • 只有NSBlockOperation放到一个NSOperationQueue中,才会异步执行
代码语言:javascript复制
- (IBAction)blockOperation:(id)sender {
    //初始化Operation子类
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        //在主线程
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [operation start];
}

blockOperationlog.png

  • NSBlockOperation 通过 addExecutionBlock: 就可以为 NSBlockOperation 添加额外的操作。这些操作(包括 blockOperationWithBlock 中的操作)可以在不同的线程中同时(并发)执行
  • 只要NSBlockOperation封装的操作数大于1,就会异步执行

注: 如果添加的操作多的话,blockOperationWithBlock: 中的操作也可能会在其他线程(非当前线程)中执行,这是由系统决定的,并不是说添加到 blockOperationWithBlock: 中的操作一定会在当前线程中执行

代码语言:javascript复制
- (IBAction)blockOperation:(id)sender {
    //初始化Operation子类
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        //在主线程
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
  
    //添加额外的任务(在子线程执行)
    [operation addExecutionBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"4--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"5--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"6--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"7--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"8--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"9--%@",[NSThread currentThread]);
    }];
    [operation start];
}

blockOperationlog.png

自定义子类继承NSOperation

  • 使用自定义子类继承NSOperation,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
  • 通过重写 main(非并发) 或者 start(并发) 方法来定义自己的 NSOperation 对象
  • 重写main方法比较简单,不需要管理操作的状态属性 isExecuting 和 isFinished
  • 当 main 执行完返回的时候,这个操作就结束了
代码语言:javascript复制
#import <Foundation/Foundation.h>
@interface SSOperation : NSOperation
@end

#import "SSOperation.h"
@implementation SSOperation
#pragma mark-需要执行的任务
-(void)main{
    if (!self.isCancelled) {
        NSLog(@"0--%@",[NSThread currentThread]);
    }
}
@end


- (IBAction)SSOperationOperation:(id)sender {
    SSOperation *operation = [[SSOperation alloc]init];
    [operation start];
}

SSOperationlog.png

关于NSOperationQueue

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

主队列

代码语言:javascript复制
- (IBAction)creatOperationQueue1:(id)sender {    
    //主队列
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    //创建操作(任务)
    //创建--NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
    //创建--NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    //自定义(需要继承NSOperation,执行的操作需要放在这个自定义类的main中)
    SSOperation *op5 = [[SSOperation alloc]init];
    //添加任务队列中
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
    //也可以直接创建任务到队列中去
    [queue addOperationWithBlock:^{
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];
}
-(void)run1{
    NSLog(@"-run1--%@",[NSThread currentThread]);
}
-(void)run2{
    NSLog(@"-run2--%@",[NSThread currentThread]);
}

主队列log.png

自定义队列(系统会自动异步执行NSOperation中的操作)

  • 使用 NSOperation 子类创建操作,并使用 addOperation: 将操作加入到操作队列后能够开启新线程,进行并发执行
代码语言:javascript复制
- (IBAction)creatOperationQueue1:(id)sender {
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //创建操作(任务)
    //创建--NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
    //创建--NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    //自定义队列(需要继承NSOperation,执行的操作需要放在这个自定义类的main中)
    SSOperation *op5 = [[SSOperation alloc]init];
    //添加任务队列中
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
    //也可以直接创建任务到队列中去
    [queue addOperationWithBlock:^{
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];
}
-(void)run1{
    NSLog(@"-run1--%@",[NSThread currentThread]);
}
-(void)run2{
    NSLog(@"-run2--%@",[NSThread currentThread]);
}

自定义队列log.png

NSOperationQueue设置最大并发操作数

  • 通过属性maxConcurrentOperationCount,叫做最大并发操作数。用来控制一个特定队列中可以有多少个操作同时参与并发执行
  • 这里 maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行
  • maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行。
  • maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
  • maxConcurrentOperationCount 大于1时,队列为并发队列。操作并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整为 min{自己设定的值,系统设定的默认最大值}
代码语言:javascript复制
//maxConcurrentOperationCount = 1
- (IBAction)creatOperationQueue2:(id)sender {
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];    
    //设置为1就成了串行队列
    queue.maxConcurrentOperationCount = 1;
    [queue addOperationWithBlock:^{
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"-4--%@",[NSThread currentThread]);
    }];
}

maxConcurrentOperationCount = 1

代码语言:javascript复制
//maxConcurrentOperationCount = 2
- (IBAction)creatOperationQueue2:(id)sender {
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //设置最大并发操作数(不管加入队列有多少操作,实际队列并发数为2)
    queue.maxConcurrentOperationCount = 2;
    
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-4--%@",[NSThread currentThread]);
    }];
}

maxConcurrentOperationCount = 2

NSOperationQueue设置队列挂起与取消

  • 当队列调用了队列挂起的方法( self.queue.suspended = YES),队列里的执行方法立即停止,但是有一点需要注意的是,当block操作中,队列挂起是不起作用的,它是无法停止的,必须操作执行结束后才会生效。
  • 当队列调用取消( [self.queue cancelAllOperations])就意味着后续队列不再执行,再次启动需要重新加入队列
代码语言:javascript复制
- (IBAction)createOperationQueueSuspended:(id)sender {
    //创建队列
    self.queue = [[NSOperationQueue alloc]init];
    self.queue.maxConcurrentOperationCount = 1;
    [self.queue addOperationWithBlock:^{
        NSLog(@"-1--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [self.queue addOperationWithBlock:^{
        NSLog(@"-2--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [self.queue addOperationWithBlock:^{
        NSLog(@"-3--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [self.queue addOperationWithBlock:^{
        NSLog(@"-4--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    //设置队列挂起或者取消的话都必须是在block方法执行完之后才有效
    [self.queue addOperationWithBlock:^{
        for(NSInteger i = 0;i<5;i  ){
            NSLog(@"-5--%zd---%@",(long)i,[NSThread currentThread]);
        }
    }];
}
#pragma mark-设置队列挂起
- (IBAction)operationSetSuspended:(id)sender {
    if(self.queue.suspended){
        //恢复队列,继续执行
        self.queue.suspended  = NO;
    }else{
        //挂起(暂停队列)
        self.queue.suspended  = YES;
    }
}
#pragma mark-设置队列取消(取消就意味着后续队列不再执行,再次启动需要重新加入队列)
- (IBAction)operationSetCancel:(id)sender {
    [self.queue cancelAllOperations];
}

NSOperationQueue设置队列依赖

代码语言:javascript复制
- (IBAction)createOperationQueueSetDependency:(id)sender {

    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
 
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down3---%@",[NSThread currentThread]);
    }];
    
    //设置依赖(op1和op3执行完之后才执行2)
    [op2 addDependency:op1];
    [op2 addDependency:op3];

    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];    

    //监听一个操作的执行完成
    [op2 setCompletionBlock:^{
        NSLog(@"执行完成");
    }];
}

注:一定要避免相互依赖,比如

代码语言:javascript复制
[op3 addDependency:op1];
[op1 addDependency:op3];    //错误的写法---相互依赖

设置队列依赖.png

监控NSOperation对象的属性

  • isExecuting 代表任务正在执行中
  • isFinished 代表任务已经执行完成,被取消也算执行完成 注:该状态关系到依赖其的操作任务,只有在其isFinished状态为YES的时候,依赖其的操作任务才能开始执行,操作队列也是根据这个状态来决定是否将操作任务从队列中移除
  • isCancelled 代表任务已经取消执行
  • isAsynchronous 代表任务是并发还是同步执行, 注:当操作任务加入到操作队列后,会忽略该属性
  • isReady 代表任务是否已经准备执行 注:当其依赖的操作任务都执行完时,改状态才会是YES

NSOperation在队列里的优先级

  • iOS8以前,NSOperation通过设置queuePriority属性来设置优先级
  • iOS 8.0后,NSOperation通过设置qualityOfService来设置优先级
  • 优先级高的先执行,低的后执行
代码语言:javascript复制
//iOS8以前的优先级(queuePriority)
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

//iOS8以后的优先级(qualityOfService)
typedef NS_ENUM(NSInteger, NSQualityOfService) {
     NSQualityOfServiceUserInteractive = 0x21,//最高优先级,用于用户交互事件
    NSQualityOfServiceUserInitiated = 0x19,//次高优先级,用于用户需要马上执行的事件
    NSQualityOfServiceUtility = 0x11,//默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
    NSQualityOfServiceBackground = 0x09,//普通优先级,用于普通任务
    NSQualityOfServiceDefault = -1//最低优先级,用于不重要的任务
}
代码语言:javascript复制
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down2---%@",[NSThread currentThread]);
        
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down3---%@",[NSThread currentThread]);
        
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down4---%@",[NSThread currentThread]);
    }];
 
    op3.qualityOfService = NSQualityOfServiceUserInteractive;

    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];

优先级.png

0 人点赞