文章目录
一、NSOperation简介
二、NSOperation和NSOperationQueue的基本使用
1、创建任务
2、创建队列
3、将任务加入到队列中
三、控制串行执行和并行执行的关键
四、操作依赖
五、一些其他方法
一、NSoperation的简介
NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。
NSoperation 总共有两个子类 : NSInvocationOperation 和 NSBlockOperation。
NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。
因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:
1、创建任务:先将需要执行的操作封装到一个NSOperation对象中。
2、创建队列:创建NSOperationQueue对象。
3、将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中。
之后呢,系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。
注意和GCD 类比学习 : Operation 相当于任务、操作队列就和GCD其实是同个意思。总归来说,NSOperation 比GCD 更有自主性!例如: 可以通过继承重写main方法自定义任务、可暂停任务、可恢复任务、可通过KVO监Operation是否完成或取消,可设置任务权重优先执行任务、可设置等待等
下面我们来学习下NSOperation和NSOperationQueue的基本使用。
二、NSOperation和NSOperationQueue的基本使用
NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务。我们有三种方式来封装任务:
1、使用 NSInvocationOperation
2、使用 NSBlockOperation
3、自定义子类继承NSoperation,并重写main方法
1、在不使用NSOperationQueue,单独使用NSOperation的情况下系统同步执行操作,下面我们学习以下任务的三种创建方式。
1 ) 使用 NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
// 调用start 方法执行操作
[operation start];
//其中run方法的定义如下
- (void)run
{
NSLog(@"NSInvocationOperation -> 当前线程:%@", [NSThread currentThread]);
}
运行结果 :
说明 :从中可以看到,在没有使用队列(NSOperationQueue),单独使用NSInvocationOperation的情况下,NSInvocationOperation在主线程执行操作,并没有开启新线程。
.
.
.
2 ) 使用 NSBlockOperation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperation -> 当前线程:%@", [NSThread currentThread]);
}];
// NSBlockOperation 提供 addExecutionBlock,可通过这个进行添加额外的操作,这些额外操作在有可能在主线程中执行,也有可能会开启子线程去执行。
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation 额外操作任务1:%@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation 额外操作任务2:%@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperation 额外操作任务3:%@", [NSThread currentThread]);
}];
[blockOperation start];
运行结果 :
说明 :
<1> 在没有使用NSOperationQueue、单独使用NSBlockOperation的情况下,NSBlockOperation也是在主线程执行操作,并没有开启新线程
<2> NSBlockOperation 提供 addExecutionBlock,可通过这个进行添加额外的操作,这些额外操作在有可能在主线程中执行,也有可能会开启子线程去执行
.
.
.
3 ) 自定义子类继承NSoperation,并重写main方法
先定义一个继承自NSOperation的子类,重写main方法
CustomOperation.h
#import <Foundation/Foundation.h>
@interface CustomOperation : NSOperation
@end
CustomOperation.m
#import "CustomOperation.h"
@implementation CustomOperation
- (void)main
{
for (int i = ; i < ; i++) {
NSLog(@"定义继承自NSOperation的子类封装任务:%@",[NSThread currentThread]);
}
}
@end
导入头文件 CustomOperation.h
CustomOperation *customOperation = [[CustomOperation alloc]init];
[customOperation start];
运行结果 :
说明 : 在没有使用NSOperationQueue、单独使用自定义子类的情况下,是在主线程执行操作,并没有开启新线程。
2、创建队列
和GCD中的并发队列、串行队列略有不同的是:NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。下边是主队列、其他队列的基本创建方法和特点。
主队列
凡是添加到主队列的任务,必然是放在主线程中执行
其它队列
添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行,同时包含了:串行、并发功能
3、将任务添加到队列中去
// 1、 创建 队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2、定义任务
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = ; i < ; i++) {
NSLog(@"NSBlockOperation任务:%@",[NSThread currentThread]);
}
}];
// 3、 将任务添加到队列中
[queue addOperation:invocationOperation];
[queue addOperation:blockOperation];
运行结果 :
说明 :NSInvocationOperation和NSOperationQueue结合后能够开启新线程,进行并发执行NSBlockOperation和NSOperationQueue也能够开启新线程,进行并发执行。
当然,你也可以无需先创建任务,可以在block中添加任务,直接将block中的人添加到队列中。
[queue addOperationWithBlock:^{
for (int i = ; i < ; i++) {
NSLog(@"任务:%@",[NSThread currentThread]);
}
}];
运行结果 :
.
.
.
三、控制串行执行和并行执行的关键
之前我们说过,NSOperationQueue创建的其他队列同时具有串行、并发功能,上边我们演示了并发功能,那么他的串行功能是如何实现的?
这里有个关键参数maxConcurrentOperationCount,这个官方的解释是如下
The maximum number of queued operations that can execute at the same time.
中文可译为 : 可以同时执行的队列操作的最大数量
maxConcurrentOperationCount 默认值为-1,表示不进行限制,默认为并发执行
当 maxConcurrentOperationCount = 1,串行执行任务
当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。
注意 : 这个参数主要控制队列是串行执行任务还是并行执行任务,具体开启的线程数交由系统控制。
具体看下面一个例子 :
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//queue.maxConcurrentOperationCount = -1;
queue.maxConcurrentOperationCount = ;
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"3-----%@", [NSThread currentThread]);
}];
当 maxConcurrentOperationCount = -1 时控制台输出
当 maxConcurrentOperationCount = 1 时控制台输出
可以看出:当最大并发数为1时,任务是按顺序串行执行的。当最大并发数为2时,任务是并发执行的。而且开启线程数量是由系统决定的,不需要我们来管理。这样看来,是不是比GCD还要简单了许多?
四、设置操作依赖
NSOperation和NSOperationQueue最吸引人的地方是它能添加操作之间的依赖关系。比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A。具体如下:
- (void)addOperationDepedency
{
// 1、 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2、 创建任务
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];
//让op2 依赖于 op1,则先执行op1,在执行op2
[op2 addDependency:op1];
// 3、 将任务添加到队列中
[queue addOperation:op2];
[queue addOperation:op1];
}//添加操作依赖
运行结果 :
.
.
.
五、一些其他方法
NSOperation提供的方法,可取消单个操作
- (void)cancel;
NSOperationQueue提供的方法,可以取消队列的所有操作
- (void)cancelAllOperations;
可设置任务的暂停和恢复,YES代表暂停队列,NO代表恢复队列
- (void)setSuspended:(BOOL)b;
判断暂停状态
- (BOOL)isSuspended;
注意:
这里的暂停和取消并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。
相关链接 :
本文Demo传送门
GCD和NSOperation的区别和选用