1 简介
NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。
NSoperation 总共有两个子类 : NSInvocationOperation 和 NSBlockOperation。
NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。因为NSoperation是基于GCD的,所以NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:
2 NSOperation和NSOperationQueue的基本使用
NSOperation是个抽象类,并不能封装任务。我们只有使用它的子类来封装任务。我们有三种方式来封装任务:
1、使用 NSInvocationOperation
2、使用 NSBlockOperation
3、自定义子类继承NSoperation,并重写main方法
//NSOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
[operation start];
- (void)run {
NSLog(@"NSInvocationOperation -> 当前线程:%@", [NSThread currentThread]);
}
运行结果为:
从上述我们可以看到NSInvocationOperation单独使用时,是在主线程中进行的,并没有开启子线程.
NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSInvocationOperation -> 当前线程:%@", [NSThread currentThread]);
}];
[block addExecutionBlock:^{
NSLog(@"其他操作");
}];
NSBlockOperation同理,单独使用也是不会在开辟线程的.NSBlockOperation提供 addExecutionBlock,可通过这个进行添加额外的操作,这些额外操作在有可能在主线程中执行,也有可能会开启子线程去执行
#import "CustomOperation.h"
@implementation CustomOperation
-(void)main {
for (int i = 0; i <3; i ++) {
NSLog(@"当前线程:%@", [NSThread currentThread]);
}
}
@end
CustomOperation *customOperation = [[CustomOperation alloc]init];
[customOperation start];
打印结果
在没有使用NSOperationQueue、单独使用自定义子类的情况下,是在主线程执行操作,并没有开启新线程。
3 创建队列
和GCD中的并发队列、串行队列略有不同的是:NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。
主队列:凡是添加到主队列的任务,必然是放在主线程中执行.
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
其他队列:添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行,同时包含了:串行、并发功能.
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
讲任务添加到队列中去.
// 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 = 0; i < 2; i++) {
NSLog(@"NSBlockOperation任务:%@",[NSThread currentThread]);
}
}];
// 3、 将任务添加到队列中
[queue addOperation:invocationOperation];
打印结果.
说明 :NSInvocationOperation和NSOperationQueue结合后能够开启新线程,进行并发执行NSBlockOperation和NSOperationQueue也能够开启新线程,进行并发执行。
4 控制串行和并行maxConcurrentOperationCount
当maxConcurrentOperationCount 默认值为-1,表示不进行限制,默认为并发执行
当 maxConcurrentOperationCount = 1,串行执行任务
当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。
注意 : 这个参数主要控制队列是串行执行任务还是并行执行任务,具体开启的线程数交由系统控制。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = -1;
// queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"3-----%@", [NSThread currentThread]);
}];
当maxConcurrentOperationCount为-1时,打印结果
当maxConcurrentOperationCount为1时,打印结果
5 设置操作依赖
NSOperation和NSOperationQueue最吸引人的地方是它能添加操作之间的依赖关系。比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A。具体如下:
// 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];
6 GCD和NSOperation的区别
- GCD是底层的C语言构成的API,而NSOperationQueue及相关对象是Objc的对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构;而Operation作为一个对象,为我们提供了更多的选择;
- 在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的block(其实是有的,但需要许多复杂的代码);
- NSOperation能够方便地设置依赖关系,我们可以让一个Operation依赖于另一个Operation,这样的话尽管两个Operation处于同一个并行队列中,但前者会直到后者执行完毕后再执行;
- 我们能将KVO应用在NSOperation中,可以监听一个Operation是否完成或取消,这样子能比GCD更加有效地掌控我们执行的后台任务;
- 在NSOperation中,我们能够设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行,而在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务的优先级,也需要大量的复杂代码;
- 我们能够对NSOperation进行继承,在这之上添加成员变量与成员方法,提高整个代码的复用度,这比简单地将block任务排入执行队列更有自由度,能够在其之上添加更多自定制的功能。