前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >block与GCD--43:NSOperation 与NSOperationQueue

block与GCD--43:NSOperation 与NSOperationQueue

作者头像
xy_ss
发布2023-11-22 08:31:20
1290
发布2023-11-22 08:31:20
举报
文章被收录于专栏:浮躁的喧嚣

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • NSOperation
    • 关于NSInvocationOperation
      • 关于NSBlockOperation
        • 自定义子类继承NSOperation
        • 关于NSOperationQueue
          • 主队列
            • 自定义队列(系统会自动异步执行NSOperation中的操作)
              • NSOperationQueue设置最大并发操作数
                • NSOperationQueue设置队列挂起与取消
                  • NSOperationQueue设置队列依赖
                    • 监控NSOperation对象的属性
                      • NSOperation在队列里的优先级
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档