美文网首页
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