作者:Mitchell
一、NSOperation简介
- NSOperation 的作用
配合使用 NSOperation 和 NSOperationQueue 也能实现多线程编程 - NSOperation 和 NSOperationQueue 实现多线程的具体步骤
- 先将需要执行的操作封装到一个 NSOperation 对象中
- 然后将 NSOperation 对象添加到 NSOperationQueue 中
- 系统会自动将 NSOperationQueue 中的 NSOperation 取出来
- 将取出来的 NSOperation 封装的操作放到一条新线程中执行
- 简单创建:
- 创建队列
- 自己创建:alloc/init --> 默认是并发 -->也可以串行
- 主对列:mainQueue
- 因为NSOperation 没有添加方法不能封装操作所以不能直接使用,两种任务方式:
NSInvocationOperation、NSBlockOperation
- 创建队列
二、NSInvocationOperation
- 因为 NSOperation 没有添加操作方法,所以 NSOperation 不能直接使用,使用 NSInvocationOperation、NSBlockOperation来创建:
- NSInvocationOperation 创建步骤:
- 1、将操作封装到 Operation 中
- 2、执行封装的操作
- 注意:如果直接执行 NSInvocationOperation 中的操作,那么默认会在主线程中执行
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(demo) object:nil];
[op1 start];
三、NSBlockOperation
- 创建过程
- 1、将操作封装到Operation中
- 2、添加操作
- 注意:
- 如果只封装了一个操作,那么默认会在主线程执行
- 如果封装了多个操作,那么除了第一个操作以外,其他的操作会在线程执行
//1、将操作封装到Operation中
NSBlockOperation*op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 - %@",[NSThread currentThread]);
}];
//2、添加操作
//如果只封装了一个操作,那么默认会在主线程执行
//如果封装了多个操作,那么除了第一个操作以外,其他的操作会在子线程执行
[op addExecutionBlock:^{
NSLog(@"2 - %@",[NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"3 - %@",[NSThread currentThread]);
}];
四、自定义Operation
- 封装的操作:
- 如果是自定义类继承与 NSOperation,那么需要将操作写到自定义类的 main 方法中
- 这种实现方式,可以提高代码的复用性
(可以将 block 中代码的实现写在 main 函数内)
MitchellOperation*op = [[MitchellOperation alloc]init];
[op start];
#import "MitchellOperation.h"
@implementation MitchellOperation
-(void)main{
//在这里自定义耗时的操作
NSLog(@"%s%@",__func__,[NSThread currentThread]);
}
@end
五、NSOperationQueue
- 创建顺序:
- 封装任务
- 将任务添加到队列
- 注意:
- 只要将任务添加到自己创建的队列中,那么队列内部会自动调用 start 。
- 只要将任务添加到队列中,就会开启一个新的线程执行队列
- 根据传入的block,创建一个 NSBlockOperation 对象
- 将创建好的 NSBlockOperation 对象,添加到队列中
1、创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
2、封装任务
2.1 NSInvocationOperation
NSInvocationOperation*op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(demo) object:nil];
2.2 NSBlockOperation
NSBlockOperation*op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
3、将任务添加到队列
[queue addOperation:op1];
[queue addOperation:op2];
六、NSOperationQueue 的串行与并发
- 默认是并发
- 如果想实现串行,那么设置
maxConcurrentOperationCount = 0
- 注意:最大并发数,不能设置位0,否则任务不会被执行,如果想在主线程中执行任务,那么直接创建 mainQueue 即可。
七、NSOperationQueue 暂停、恢复、取消
- 暂停队列中的任务
- YES 需要暂停
- NO 恢复执行
-
注意:
- 如果再任务执行的过程中暂停队列的任务,那么当前正在执行的任务并不会被暂停,而是会暂停队列中的下一个任务。
- 恢复任务,是从队列第一个没有被执行过的任务开始恢复。
self.queue.suspended = !self.queue.suspended;
+取消任务:
- 注意:
* 取消任务和暂停任务一样,不会取消当前正在执行的任务。
* 所以当执行耗时操作的时候,每执行一段代码之后就应该判断一下当前操作是否被取消,可以节约性能。
[self.queue cancelAllOperations];
八、NSOperationQueue 任务间的依赖和监听
- 依赖:
[op2 addDependency:op1];
- 监听:
op1.completionBlock = ^(){
NSLog(@"图片1下载完毕");
};
- 实例
NSOperationQueue*queue = [[NSOperationQueue alloc]init];
//下第一张图片
NSOperation*op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载1");
}];
//下第二张图片
NSOperation*op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载2");
}];
NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载3");
//回主线程渲染图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"渲染");
}];
}];
//监听任务是否执行完毕
op1.completionBlock = ^(){
NSLog(@"图片1下载完毕");
};
op2.completionBlock = ^(){
NSLog(@"图片2下载完毕");
};
//添加依赖
//只要添加了依赖,那么就会等到依赖的任务执行完毕,才会执行当前任务
//注意:
//1、添加依赖,不能添加循环依赖
//2、NSOperation可以跨队列添加依赖
[op2 addDependency:op1];
[op3 addDependency:op2];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];