美文网首页
iOS - 多线程NSOperation和NSOperation

iOS - 多线程NSOperation和NSOperation

作者: ShIwEn9 | 来源:发表于2019-08-10 11:29 被阅读0次

求职广告 (2019 - 12):因上一家公司资金链,全员被迫离职,现在需要一份iOS开发的工作,杭州南京合肥的都可以。对我感兴趣的可以私聊我 0.0。谢谢~~~

一、NSOperation和NSOperationQueue介绍
    • NSOperation: 操作(任务)
    • NSOperationQueue: 操作队列(队列):分为主队列和自定义队列,是用来存放操作的队列,遵循FIFO(先进先出)的原则。通过最大并发数控制串行并发
    • 实际上NSOperation和NSOperationQueue是OC语言基于GCD更高一层封装
    • 使用起来比GCD更加的简单
    • 提供了一些用GCD不好实现的功能
    • 和GCD一样,我们不必去关心线程,以及线程的生命周期
    • NSOperation是一个抽象的类,我们不能够直接去使用NSOperation,把NSOperation当作父类,起到约束子类的作用 ,然后通过去实现其子类来实现功能
    • NSOperation的子类
      • 子类 NSInvocationOperation
      • 子类 NSBlockOperation
      • 自定义继承自 NSOperation 的子类,通过实现内部相应的方法来封装操作。
  1. 一般情况下,通过NSOperation和NSOperationQueue实现的都是异步操作。
二、NSOperation和NSOperationQueue使用
  1. 通过子类NSInvocationOperation去执行线程操作
  • NSInvocationOperation继承自NSOperation,并拥有自己的一些方法
  • start方法是父类NSOperation中的,所以无论是NSInvocationOperation还是NSBlockOperation执行的效果都是一样的。
    start: 1.不回去开启新线程 回调用 main函数 2.更新操作的状态
    // 1. 调用start方法
    // 创建操作
    NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(demo) object:nil];
    NSLog(@"%d",op.finished);
    //start: 1.不回去开启新线程 回调用 main函数
    //        2.更新操作的状态
    [op start];
    NSLog(@"%d",op.finished);
    
    // 2. 将操作添加到操作队列中
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(demo) object:nil];
    NSOperationQueue *opQueue = [[NSOperationQueue alloc]init] ;
    // addOperation需要添加NSOperation类的实例化对象,但是这里添加NSInvocationOperation的实例化对象,是因为父类的指针可以指向子类
    [opQueue addOperation:op1];

-(void)demo{
    NSLog(@"123 = %@",[NSThread currentThread]);
}

通过start方法运行的结果:没开新线程,在主线程上面去执行

0
123 = <NSThread: 0x600001faae40>{number = 1, name = main}
1

通过:添加到NSOperationQueue队列上面之后,开启新线程

123 = <NSThread: 0x600001fefd00>{number = 3, name = (null)}

  1. 通过子类NSBlockOperation去执行线程操作
  • 调用start方法
NSBlockOperation *blockOP = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"initNSBlockOperation = %@",[NSThread currentThread]);
    }] ;
    
    // 通过start方法
    // 和NSInvocationOperation中的效果一样,因为start是父类NSOperation的方法
    [blockOP start] ;

效果和NSInvocationOperation一样,不会开启新线程,在主线程上执行。

<NSThread: 0x6000008c2480>{number = 1, name = main}

  • 添加到其他队列中
    • 通过addExecutionBlock添加额外了操作
NSOperationQueue *queue = [[NSOperationQueue alloc]init] ;
    NSBlockOperation *blockOP = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"initNSBlockOperation = %@",[NSThread currentThread]);
    }];
    // 添加额外的操作
    [blockOP addExecutionBlock:^{
        NSLog(@"addExecutionBlock = %@",[NSThread currentThread]);
    }];
    
    // 父类的指针可以指向子类
    [queue addOperation:blockOP];

都是默认最大并发数为-1的时候
initNSBlockOperation = <NSThread: 0x6000037596c0>{number = 3, name = (null)}
addExecutionBlock = <NSThread: 0x60000374b040>{number = 4, name = (null)}

  1. 只通过NSOperationQueue去实现
    • 一般我们都回去定义全局的NSOperationQueue对象
@property(nonatomic,strong)NSOperationQueue *queue;
...

-(NSOperationQueue *)queue{
    if (_queue == nil) {
        _queue = [[NSOperationQueue alloc]init];
//        self.queue.maxConcurrentOperationCount =2;
    }
    return _queue;
}
...
for (int i = 0; i < 10; i++) {
        [self.queue addOperationWithBlock:^{
            NSLog(@"Demo3 = %d %@",i,[NSThread currentThread]);
        }] ;
    }

异步并发执行,开启新线程,并发执行

Demo3 = 2 <NSThread: 0x600003596680>{number = 5, name = (null)}
Demo3 = 1 <NSThread: 0x6000035882c0>{number = 3, name = (null)}
Demo3 = 0 <NSThread: 0x600003585d40>{number = 4, name = (null)}
Demo3 = 3 <NSThread: 0x600003585d00>{number = 6, name = (null)}
Demo3 = 5 <NSThread: 0x600003596680>{number = 5, name = (null)}
Demo3 = 6 <NSThread: 0x600003585d40>{number = 4, name = (null)}
Demo3 = 4 <NSThread: 0x6000035882c0>{number = 3, name = (null)}
Demo3 = 7 <NSThread: 0x600003585d00>{number = 6, name = (null)}
Demo3 = 8 <NSThread: 0x600003596680>{number = 5, name = (null)}
Demo3 = 9 <NSThread: 0x6000035882c0>{number = 3, name = (null)}

  1. 线程之间的通信
    通过[NSOperationQueue mainQueue]回到主线程中
[self.queue addOperationWithBlock:^{
        //异步下载
        
        // 获取当前的队列
        [NSOperationQueue currentQueue];
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            // 回到主线程来执行操作 更新UI
            
        }];
    }];
  1. 设置线程的最大并发数
    通过maxConcurrentOperationCount属性来实现最大并发数
    • 当maxConcurrentOperationCount = 1 的时候,为串行执行,大于 1 为并发执行。
self.queue.maxConcurrentOperationCount =2; // 一般在懒加载中写
    for (int i = 0; i< 10 ; i++) {
        [self.queue addOperationWithBlock:^{
            NSLog(@"%d=%@",i,[NSThread currentThread]);
        }];
    }
    // 队列中的操作数
    [self.queue operationCount];

1=<NSThread: 0x600003367680>{number = 4, name = (null)}
0=<NSThread: 0x600003369a00>{number = 3, name = (null)}
3=<NSThread: 0x600003367680>{number = 4, name = (null)}
2=<NSThread: 0x600003367b80>{number = 5, name = (null)}
4=<NSThread: 0x600003367680>{number = 4, name = (null)}
5=<NSThread: 0x600003367b80>{number = 5, name = (null)}
6=<NSThread: 0x600003367680>{number = 4, name = (null)}
7=<NSThread: 0x600003367b80>{number = 5, name = (null)}
8=<NSThread: 0x600003367680>{number = 4, name = (null)}
9=<NSThread: 0x600003367b80>{number = 5, name = (null)}

  1. 操作的优先级
  • 优先级是先对的,只能说优先级高的操作,被执行的几率会大,并不代表先执行谁后执行谁。
NSBlockOperation *bOp1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i=0; i<10; i++) {
            NSLog(@"bOp1 %d %@",i,[NSThread currentThread]);
        }
    }];
    /*  优先级由高到低
        NSQualityOfServiceUserInteractive = 0x21, 
        NSQualityOfServiceUserInitiated = 0x19,
        NSQualityOfServiceUtility = 0x11,
        NSQualityOfServiceBackground = 0x09,
        NSQualityOfServiceDefault = -1
    */
    bOp1.qualityOfService = NSQualityOfServiceUserInteractive;
    [self.queue addOperation:bOp1];
    
    NSBlockOperation *bOp2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i=0; i<10; i++) {
            NSLog(@"bOp2 %d %@",i,[NSThread currentThread]);
        }
    }];
    [self.queue addOperation:bOp2];

bOp1 0 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp2 0 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp2 1 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp1 1 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp1 2 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp2 2 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp1 3 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp1 4 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp2 3 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp1 5 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp1 6 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp2 4 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp1 7 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp1 8 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp1 9 <NSThread: 0x600001a6a140>{number = 3, name = (null)}
bOp2 5 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp2 6 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp2 7 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp2 8 <NSThread: 0x600001a611c0>{number = 4, name = (null)}
bOp2 9 <NSThread: 0x600001a611c0>{number = 4, name = (null)}

  1. 监听操作完成
    当操作完成之后执行再执行某操作
NSBlockOperation *bOp1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i=0; i<5; i++) {
            NSLog(@"bOp1 %d %@",i,[NSThread currentThread]);
        }
    }];
    [self.queue addOperation:bOp1];
    
    bOp1.completionBlock = ^{
        NSLog(@"完成操作");
    };

bOp1 0 <NSThread: 0x600003c98780>{number = 3, name = (null)}
bOp1 1 <NSThread: 0x600003c98780>{number = 3, name = (null)}
bOp1 2 <NSThread: 0x600003c98780>{number = 3, name = (null)}
bOp1 3 <NSThread: 0x600003c98780>{number = 3, name = (null)}
bOp1 4 <NSThread: 0x600003c98780>{number = 3, name = (null)}
完成操作

  1. 添加操作依赖
    简单理解为,若B依赖A则等到A执行完之后才去执行B,也就是它们之间有了执行的顺序。

注意防止出现循环依赖

通过 addDependency方法实现
移除依赖 removeDependency

NSBlockOperation *bOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载压缩包");
    }];
    NSBlockOperation *bOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"解压压缩包");
    }];
    NSBlockOperation *bOp3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"升级完成");
    }];
    // 操作依赖,等依赖前面的操作执行完成之后再去执行后面的操作
    [bOp2 addDependency:bOp1];
    
    [bOp3 addDependency:bOp2];
    
    // 错误操作,可能会出现循环依赖
//    [bOp1 addDependency:bOp3] ;
    
    [self.queue addOperations:@[bOp1,bOp2] waitUntilFinished:NO];
    
    // 操作依赖可以跨队列
    [[NSOperationQueue mainQueue]addOperation:bOp3];

下载压缩包
解压压缩包
升级完成

  1. 队列的取消、暂停、恢复
    设置暂停、恢复通过suspended 属性来实现
    当我们访问读取suspended的时候用isSuspended属性
// 取消 当前执行的操作不会立即取消,会执行完毕,后续的操作才会取消
    [self.queue cancelAllOperations];
    // 暂停 当前执行的操作不会立即暂停,会执行完毕,后续的操作才会暂停
    self.queue.suspended = YES;
    // 恢复
    self.queue.suspended = NO;

文章到这里就结束了,我还是有很多不懂的地方,若文中有错误内容,欢迎指正,谢谢🙏。
文章主要参考:
行走少年郎iOS 多线程:『NSOperation、NSOperationQueue』详尽总结

求职广告:本人实习生,现在急需一份工作,杭州南京合肥的都可以。对我感兴趣的可以私聊我 0.0。谢谢~~~

相关文章

网友评论

      本文标题:iOS - 多线程NSOperation和NSOperation

      本文链接:https://www.haomeiwen.com/subject/tvccjctx.html