iOS 多线程之NSOperation篇举例详解

  这篇博客是接着总篇iOS GCD NSOperation NSThread等多线程各种举例详解写的一个支篇。总篇也包含了此文的链接。本文讲解的知识点有NSBlockOperationClick,队列,队列中如何加Operation,Operation中如何加任务,Operation之间的串行、并行,监控任务完成时机及其他一些关于NSOperation的方法,每个知识点都有例子和详细分析。附上demo下载地址

一、NSOperation介绍

  NSOperation 是苹果公司对 GCD 的封装,完全面向对象。NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作。NSOperation本身是抽象基类,因此可以使用它的子类NSInvocationOperation 和 NSBlockOperation,或者自定义子类也行。NSOperation 和 NSOperationQueue 可以看成是GCD的任务和队列。

二、NSInvocationOperation(不是类型安全的,苹果在swift里不用它了)

  但是我还是来讲讲,证明它曾经存在过。NSInvocationOperation创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。举个例子

- (IBAction)NSInvocationOperationClick:(id)sender {
    //1.创建NSInvocationOperation对象
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(myTask) object:nil];
    operation.completionBlock = ^() {
        NSLog(@"执行完毕");
    };
    //2.在当前线程执行
    [operation start];
    NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]);
}

//模拟很耗时的任务
-(void)myTask {
    for (NSInteger i = 0; i < 500000000; i++) {
        if (i == 0) {
            NSLog(@"任务 -> 开始");
        }
        if (i == 499999999) {
            NSLog(@"任务 -> 完成");
        }
    }
}

 

打印结果:

分析结论:从打印结果看,会阻塞当前线程,因为他是同步执行的。

三、NSBlockOperationClick

  在block中加任务,使用更方便,代码更紧凑,举个例子。

- (IBAction)NSBlockOperationClick:(id)sender {
    //1.创建NSBlockOperation对象
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [self myTask];
    }];
    
    //2.也可以添加多个Block,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务
    for (NSInteger n = 0; n < 3; n++) {
        [operation addExecutionBlock:^{
            for (NSInteger i = 0; i < 500000000; i++) {
                if (i == 0) {
                    NSLog(@"任务%ld -> 开始",n);
                }
                if (i == 499999999) {
                    NSLog(@"任务%ld -> 完成",n);
                }
            }
        }];
    }
    operation.completionBlock = ^() {
        NSLog(@"执行完毕");
    };
    //3.开始任务
    [operation start];
    NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]);
}

打印结果:

分析结论:给 Operation 添加多个执行 Block任务,Operation 中的任务会并发执行,它会在主线程和其它的多个线程执行这些任务,会阻塞当前主线程。

四、主队列

  举个例子

//主队列里的任务并行执行,且不阻塞当前线程。(队列中加多个operation时另说,看第五个例子就知道了)
- (IBAction)mainQueue:(id)sender {
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    //创建NSBlockOperation对象
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [self myTask];
    }];
    for (NSInteger n = 0; n < 3; n++) {
        [operation addExecutionBlock:^{
            for (NSInteger i = 0; i < 500000000; i++) {
                if (i == 0) {
                    NSLog(@"主队列中任务%ld -> 开始%@",n,[NSThread currentThread]);
                }
                if (i == 499999999) {
                    NSLog(@"主队列中任务%ld -> 完成",n);
                }
            }
        }];
    }
    operation.completionBlock = ^() {
        NSLog(@"执行完毕");
    };
    //加入到队列中任务自动执行
    [queue addOperation:operation];
    NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]);
}

打印结果:

分析结论:主队列里的任务都是另开线程并行执行的,不会阻塞当前线程。(队列中加多个operation时另说,请看下面例子)

五、其他队列

  基本用法跟主队列差不多,我将在这个例子里列举更多用法。在一个队列里加2个operation,第一个operation里加2个任务。举个例子

- (IBAction)otherQueue:(id)sender {
    //1.创建一个其他队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //最大并发数,用来设置最多可以让多少个operation同时执行。当你把它设置为 1 的时候,就是串行了(指多个operation的串行,同一个operation中的任务是并行的)
    queue.maxConcurrentOperationCount = 1;
    
    //2.创建NSBlockOperation对象
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i < 500000000; i++) {
            if (i == 0) {
                NSLog(@"operation1中任务1 -> 开始");
            }
            if (i == 499999999) {
                NSLog(@"operation1中任务1 -> 完成");
            }
        }
    }];
    
    //3.给operation1再加一个任务
    [operation1 addExecutionBlock:^{
        for (NSInteger i = 0; i < 500000000; i++) {
            if (i == 0) {
                NSLog(@"operation1中任务2 -> 开始");
            }
            if (i == 499999999) {
                NSLog(@"operation1中任务2 -> 完成");
            }
        }
    }];
    operation1.completionBlock = ^() {
        NSLog(@"执行完毕");
    };
    
    //4.再加一个operation2
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i < 500000000; i++) {
            if (i == 0) {
                NSLog(@"operation2中任务 -> 开始");
            }
            if (i == 499999999) {
                NSLog(@"operation2中任务 -> 完成");
            }
        }
    }];
    
    //5.加入到队列中任务自动执行,waitUntilFinished为yes会阻塞当前线程,为no不阻塞
    [queue addOperations:@[operation1,operation2] waitUntilFinished:NO];
    NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]);
}

打印结果:

分析结论:2个operation是串行的,但同一个operation中的多个任务是并行的

六、任务依赖

  任务一完成的情况下才能执行任务二,任务二完成的情况下才能执行任务三,举个例子。

//任务依赖
- (IBAction)addDependency:(id)sender {
    //1.任务一
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务1开始");
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"任务1完成");
    }];
    //2.任务二
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务2开始");
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"任务2完成");
    }];
    //3.任务三
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务3开始");
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"任务3完成");
    }];
    //4.设置依赖
    [operation2 addDependency:operation1];      //任务二依赖任务一
    [operation3 addDependency:operation2];      //任务三依赖任务二
    //5.创建队列并加入任务
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
}

打印结果:

分析结论:该任务依赖的任务完成了,才能执行该任务。

七、其他相关方法

// 暂停queue
[queue setSuspended:YES];

// 继续queue
[queue setSuspended:NO];
 //会阻塞当前线程,等到某个operation执行完毕
[operation waitUntilFinished];
// 阻塞当前线程,等待queue的所有操作执行完毕
[queue waitUntilAllOperationsAreFinished];
// 取消单个操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];

 

posted @ 2015-11-20 13:59  张林峰  阅读(2555)  评论(0编辑  收藏  举报