iOS进阶之多线程--NSOperation

NSOperation简介

熟悉了解GCD之后,对于NSOperation的理解和使用相对简单一点。首先,NSOperation是基于GCD面向对象的封装,是一种“并发”技术,之所以是“并发”技术,后面会有详细的代码演示。

  • 队列:NSOperationQueue
  • 操作:NSOperation (下面提到的“操作”指的就是NSOperation)
    NSOperation位于Foundation系统框架下,是一个抽象类,本身不能直接使用,它的作用是为其子类提供了一些共有属性,我们所用到的都是其子类:
    NSIvocationoperation 方法选择器方式操作,将要执行的繁琐的操作封装为单独的方法,使得代码规范条理;
    NSBlockOperation Block方式操作,将要执行的简单的操作作为代码块方式直接添加,所有代码会写在一个地方,便于维护。
    NSOperation的所有子类都可以直接添加到NSOperationQueue对象中,自动立即异步执行操作。

NSOperation简单使用

  1. NSOperation的两个子类的使用
    (1)NSInvocationOperation:方法选择式
    NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"invocation"];

  - (void)download:(id)objc{
    NSLog(@"%@   %@",[NSThread currentThread],objc);
  }

start方式,会在当前线程执行调度方法

    [op start];

addOperation:方式,将操作添加到队列,这种添加方式会自动异步调度方法。为了清晰查看异步调度,我们加入循环并打印结果:

  @property (strong, nonatomic) NSOperationQueue *q;
  #pragma mark -------- 懒加载
  - (NSOperationQueue *)q{
      if (!_q) {
          _q = [[NSOperationQueue alloc]init];
      }
      return _q;
  }

    for (int i = 0; i < 10 ; i ++) {
        NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
   
    [self.q addOperation:op];    }

打印

2018-04-08 11:28:10.687493+0800 NSOperation[1308:362486] {number = 3, name = (null)}   0
2018-04-08 11:28:10.687507+0800 NSOperation[1308:362485] {number = 4, name = (null)}   1
2018-04-08 11:28:10.687496+0800 NSOperation[1308:362488] {number = 6, name = (null)}   3
2018-04-08 11:28:10.687493+0800 NSOperation[1308:362487] {number = 5, name = (null)}   2
2018-04-08 11:28:10.687844+0800 NSOperation[1308:362488] {number = 6, name = (null)}   4
2018-04-08 11:28:10.687849+0800 NSOperation[1308:362486] {number = 3, name = (null)}   5
2018-04-08 11:28:10.687864+0800 NSOperation[1308:363127] {number = 7, name = (null)}   6
2018-04-08 11:28:10.688220+0800 NSOperation[1308:363128] {number = 8, name = (null)}   7
2018-04-08 11:28:10.688495+0800 NSOperation[1308:363129] {number = 9, name = (null)}   8
2018-04-08 11:28:10.688569+0800 NSOperation[1308:362487] {number = 5, name = (null)}   9

异步调度,非顺序执行

如果同时添加几个操作可以使用addOperations

        NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
        NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
        NSInvocationOperation *op4 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
        [self.q addOperations:@[op1, op2, op3, op4]];

注意:

- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait

这个方法中有一个wait参数 意思是:这段代码之后的代码是否等待 会卡主当前线程

(2)NSBlockOperation:
NSBlockOperation同样有start、add 和addOperations 三种方式,这里不再列举,此处简单介绍实例化方式:

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

打印结果和NSInvocationOperation一样

2.NSOperationQueue直接添加操作Block,无需创建操作

    for (int i = 0; i < 10; i ++) {
        [self.q addOperationWithBlock:^{
            NSLog(@"%@  --  %d",[NSThread currentThread],i);
        }];
    }
  1. NSOperationQueueNSOperationQueue的相关属性 最大并发数:maxConcurrentOperationCount、暂停/继续suspended、队列的操作数:operationCount、取消队列所有操作:cancelAllOperations
  • maxConcurrentOperationCount:
    self.q.maxConcurrentOperationCount = 2;
    for (int i = 0; i < 10; i ++) {
        [self.q addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"%@ %d",[NSThread currentThread],i);
        }];
    }

打印

2018-04-08 14:19:47.632013+0800 NSOperation最大并发数[1694:663455] {number = 3, name = (null)} 0
2018-04-08 14:19:47.632013+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 1
2018-04-08 14:19:48.633749+0800 NSOperation最大并发数[1694:663454] {number = 5, name = (null)} 2
2018-04-08 14:19:48.633749+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 3
2018-04-08 14:19:49.634068+0800 NSOperation最大并发数[1694:663454] {number = 5, name = (null)} 4
2018-04-08 14:19:49.634194+0800 NSOperation最大并发数[1694:663455] {number = 3, name = (null)} 5
2018-04-08 14:19:50.639034+0800 NSOperation最大并发数[1694:663454] {number = 5, name = (null)} 7
2018-04-08 14:19:50.639026+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 6
2018-04-08 14:19:51.639502+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 9
2018-04-08 14:19:51.639502+0800 NSOperation最大并发数[1694:663455] {number = 3, name = (null)} 8

最大并发数设置完成之后,队列调度操作会开启子线程的条数是不确定的(原因是当子线程在执行完成操作之后会有一个回收线程的过程,在此过程中,队列调度操作仍在继续,会到线程池中拿到线程执行操作),确定的是正在执行任务的子线程的条数就是小于或等于最大并发数。
iOS8.0以前,无论使用GCD还是NSOperation,都会开启很多条线程,在iOS7.0 以前,GCD通常只会开启5~6线程!
目前线程多了,说明:
1)底层的线程池更大了,能拿到的线程资源多了
2)对控制同事并发的线程数,需求更高了

  • suspended:
        //暂停
        self.q.suspended = YES;
        //继续
        self.q.suspended = NO;

当执行暂停操作时,正在执行的操作不会被暂停

  • operationCount
  • cancelAllOperations
    1)队列挂起的时候,不会清空内部的操作。只有队列继续的时候才会清空
    2)正在执行的操作也不会被取消
  1. NSOperation依赖关系
    在GCD中,我们通过同步的方式,给多个任务添加依赖关系。在NSOperation中,苹果提供了更简单的面向Operation对象的依赖关系设置 addDependency。下面依然拿GCD中用到的例子:登录、支付、下载来演示操作之间的依赖关系设置。
    /**
     例子:登录、支付、下载
     */
    //1.登录
    NSBlockOperation *opLogIn = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"登录--%@",[NSThread currentThread]);
    }];
    //2.支付
    NSBlockOperation *opPay = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"支付--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:10.0];
    }];
    //3.下载
    NSBlockOperation *opDown = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载--%@",[NSThread currentThread]);
    }];
    //4.完成
    NSBlockOperation *opFinish = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"完成--%@",[NSThread currentThread]);
    }];
    //支付依赖于登录
    [opPay addDependency:opLogIn];
    //下载依赖于支付
    [opDown addDependency:opPay];
    [self.opQueue addOperations:@[opLogIn,opPay,opDown] waitUntilFinished:YES];
    NSLog(@"come here!%@",[NSThread currentThread]);

注意:不要设置循环依赖,循环依赖的操作不会执行;一定要在操作添加到队列之前设置依赖关系,否则无效

将完成操作放到主线程中执行,完成线程间通讯

    [[NSOperationQueue mainQueue] addOperation:opFinish];

你可能感兴趣的:(iOS进阶之多线程--NSOperation)