iOS多线程(三):NSOperation

NSOperation是基于GCD的面向对象封装,在各大开源库里面我们常常看到它的身影。它的使用很简单易懂,基本上你点进Api就会用了,本文就最常用的功能进行讲解,更多的多线程原理还是看本系列文章的GCD部分。

一、NSBlockOperation和NSInvocationOperation

这两个类都是NSOperation的子类,它们的区别可能就是一个是用block回调,一个是用NSInvocation 回调,我们通常直接使用的也是这两个类,下面代码示意如何创建任务:

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{}];

NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(respondsToOperation:) object:nil];

都不用多说了,这就是两个任务,非常简单,当然,它还有一系列的方法表示状态:executingfinishedcancelled,以及开启和取消:startcancel,和NSThread一样有main方法可重写。

二、NSOperationQueue 队列

和GCD一样,NSOperation保留了队列的概念,但是没有具体的并行串行的概念了,但是我们可以实现它。

先来看这样一段代码:

NSLog(@"主线程开始");

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务0 %@", [NSThread currentThread]);
}];
for (int i = 1; i < 5; i++) {
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务%d: %@", i, [NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    }];
}
[blockOperation start];

NSLog(@"主线程结束");
主线程开始
任务3: {number = 5, name = (null)}
任务2: {number = 4, name = (null)}
任务1: {number = 3, name = (null)}
任务0 {number = 1, name = main}
任务4: {number = 1, name = main}
主线程结束

上机打印出来的效果来看,这些任务是并行执行的,但是会阻塞主线程直到任务全部都执行完成。显然这不能满足我们的全部需求,所以我们把任务加入队列试试(加入队列任务自动执行):

NSLog(@"主线程开始");

NSOperationQueue *queue = [NSOperationQueue new];

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务0 %@", [NSThread currentThread]);
}];

for (int i = 1; i < 5; i ++) {
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务%d %@", i, [NSThread currentThread]);
    }];
}

[queue addOperation:blockOperation];

//[blockOperation waitUntilFinished];

NSLog(@"主线程结束");
主线程开始
主线程结束
任务0 {number = 3, name = (null)}
任务1 {number = 5, name = (null)}
任务4 {number = 4, name = (null)}
任务3 {number = 3, name = (null)}
任务2 {number = 5, name = (null)}

从打印结果来看,这是并行队列+异步任务,这基本满足我们后台执行耗时任务的需求了。

从打印结果来看,这是并行队列+异步任务,这基本满足我们后台执行耗时任务的需求了。

你们应该也注意到了我注释了一句代码[blockOperation waitUntilFinished];,现在将它打开,运行得到的结果和方法的名字一样,它会阻塞当前线程直到任务全部结束,当然NSOperationQueue也有这么一个方法:waitUntilAllOperationsAreFinished

maxConcurrentOperationCount

最大并发数,通过设置这个我们可以有效的控制并发任务的数量,从而对性能进行有效的控制。

三、addDependency 添加任务依赖

我们给NSOperation添加依赖的目的,主要是实现串行队列的效果。

NSLog(@"主线程开始");

NSOperationQueue *queue = [NSOperationQueue new];

NSBlockOperation *operation0 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务1: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务2: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务3: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务4: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

[operation1 addDependency:operation0];
[operation2 addDependency:operation1];
[operation3 addDependency:operation2];
//不要添加相互依赖。理论上不管任务在任何队列都可以添加依赖,不过不建议这么做。

[queue addOperations:@[operation0, operation1, operation2, operation3] waitUntilFinished:YES];

NSLog(@"主线程结束");
主线程开始
任务1: {number = 3, name = (null)}
任务2: {number = 4, name = (null)}
任务3: {number = 3, name = (null)}
任务4: {number = 4, name = (null)}
主线程结束

实验证明,达到了我们预期的效果,任务1到任务4都是串行执行的,并且addOperations: waitUntilFinished:方法若第二个参数为YES,会阻塞主线程。

写在后面

NSOperation的用法不见得有GCD简洁,但它符合面向对象的编程思想,而且便于在我们的业务代码中穿梭,所以它的使用场景还是比较多的,没什么难点可言,本文列举的用法已经可言解决大多数的场景了,如果想多了解,直接看一遍苹果官网的API就OK了?。

你可能感兴趣的:(iOS多线程(三):NSOperation)