OC_NSOperation
](http://www.jianshu.com/p/4b1d77054b35)
1. NSOperation 简介
- NSOperation 是苹果提供给我们的一套多线程解决方案;实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性更高。
-
NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线层的能力,只有配合NSOperationQueue才能实现异步执行。
-
因为NSOperation是基于GCD的,所以使用起来和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列,NSOperation实现多线程分为三个步骤:
- 创建任务:现将需要执行的操作封装到一个NSOperation对象中,
- 创建队列:创建NSOperationQueue对象。
- 将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中。
- 之后,系统会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。
2. NSOperation和NSOperationQueue的基本使用
2.1 创建任务
- NSOperation 是个抽象类,并不能封装任务,我们只有使用它的子类来封装任务。有以下三种方式:
- 使用子类NSInvocationOperation;
- 使用子类NSBlockOperation;
- 定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。
注:在不使用NSOperationQueue,单独使用NSOperation的情况下系统同步执行操作。
2.1.1 使用子类NSInvocationOperation
创建任务:
//1. 创建NSInvocationOperation对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//2. 调用start方法开始执行操作
[op start];
- (void)run
{
NSLog(@"---------%@",[NSThread currentThread]);
}
Paste_Image.png
- 可以看出在没有使用NSOperationQueue、单独使用NSInvocationOperation的情况下,NSInvocationOperation在主线程执行操作,并没有开启新线程。
2.1.2 使用子类NSBlockOperation
创建任务
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//要执行的任务
NSLog(@"------------%@",[NSThread currentThread]);
}];
[op start];
Paste_Image.png
-
同样可以看到,在没有使用NSOperationQueue,单独使用NSBlockOperation时,也是在主线程中执行操作的,并没有开启新线程。
-
但是,NSBlockOperation 还提供了一个方法
addExecutionBlock:
,通过addExecutionBlock:
就可以为NSBlockOperation添加额外的操作,这些额外的操作就会在其他线程并发执行。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//在主线程执行
NSLog(@"1-------%@", [NSThread currentThread]);
}];
//添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"2---------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"3-----------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"4------------%@",[NSThread currentThread]);
}];
[op start];
}
- (void)run
{
NSLog(@"--------%@", [NSThread currentThread]);
}
/**
*
*NSBlockOperation创建人物
*/
- (void)blockOpCreate
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//要执行的任务
NSLog(@"------------%@",[NSThread currentThread]);
}];
[op start];
}
Paste_Image.png
- 可以看出:
blockOperationWithBlock:
方法中的操作是在主线程执行的,而addExcutionBlock:
方法中的操作实在其他线程中执行的。
2.1.3 定义继承自NSOPeration的子类
- 先定义一个继承自NSOperation的子类,重写main方法
/**
* 需要执行的任务
*/
- (void)main{
for (int i = 0; i < 2; ++i) {
NSLog(@"1--------%@", [NSThread currentThread]);
}
}
2.使用的时候导入头文件LCHOperation
LCHOperation *op = [[LCHOperation alloc] init];
[op start];
运行结果:
Paste_Image.png
结论: 在没有使用OPerationQueue、单独使用自定义子类创建任务的情况下,是在主线程执行操作的,并没有开启新线程。
2.2 NSOperationQueue的创建
- 和GCD中的并发的队列、串行队列略有不同的是:
NSOperationQueue
一共有两种队列:主队列、其他队列。其中其他队列包括串行、并发功能。下边是主队列、其他队列的基本创建方法和特点。
- 主队列:
- 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行
NSOperationQueue *queue = [NSOperation mainQueue];
- 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行
- 其他队列(非主队列)
- 添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行
- 同时包含了:串行、并发功能
NSOperation *queue = [[NSOperation alloc] init];
2.3 将任务加入到队列中
- 前边说了,NSOperation需要配合NSOperationQueue来实现多线程。
那么我们需要将创建好的任务加入到队列中去。有以下两种方法:
- 方法1:
- (void)addOperation:(NSOperation *)op;
- 需要先创建任务,再创建好的任务加入到创建好的队列中去
- (void)addOperationToQueue
{
//1. 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2. 创建操作
//创建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//创建NSBlockOperation
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1-------%@", [NSThread currentThread]);
}
}];
//3. 添加操作到队列中:addOperation
[queue addOperation:op1];
[queue addOperation:op2];
}
- (void)run
{
// NSLog(@"--------%@", [NSThread currentThread]);
for (int i = 0; i < 2; ++i) {
NSLog(@"--------%@", [NSThread currentThread]);
}
}
运行结果:
![Paste_Image.png](https://img.haomeiwen.com/i2897594/b978a6c16651510b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>结论:NSInvocationOperation和NSOperationQueue结合后能够开启新线程并行执行任务;NSBlockOperation和NSOperationQueue结合使用也一样。
- 方法2:`- (void)addOperationWithBlock:(void (^)(void))block;`
- 无需先创建任务,在block中添加任务,直接将任务block加入到队列中。
```
- (void)addOperationToBlockQueue
{
//1. 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2. 添加操作到队列中:addOperationWithBlock:
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; ++i) {
NSLog(@"--------%@", [NSThread currentThread]);
}
}];
}
```
运行结果:
![Paste_Image.png](https://img.haomeiwen.com/i2897594/31694d3f1bb4077e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>- 结论:addOperationWithBlock:和NSOperationQueue能够开启新线程,进行并发执行。
###3. 控制串行执行和并行执行的关键
- 关键参数:`maxConcurrentOperationCount`(最大并发数)
- `maxConcurrentOperationCount`默认情况下为-1,表示不进行限制,默认为并发执行。
- 当`maxConcurrentOperationCount`为1时,进行**串行执行**。
- 当`maxConcurrentOperationCount`大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会进行自动调整。
- 代码演示:
/**
- 串行执行、并行执行演示(通过控制maxConcurrentOperationCount实现)
*/
-
(void)operationQueue
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];//设置最大并发操作数
queue.maxConcurrentOperationCount = 1; //设为1时串行执行
// queue.maxConcurrentOperationCount = 2; //大于1时并行执行//添加操作
[queue addOperationWithBlock:^{
NSLog(@"1----------%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];[queue addOperationWithBlock:^{
NSLog(@"2----------%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];[queue addOperationWithBlock:^{
NSLog(@"3----------%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];[queue addOperationWithBlock:^{
NSLog(@"4----------%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];[queue addOperationWithBlock:^{
NSLog(@"5----------%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];[queue addOperationWithBlock:^{
NSLog(@"6----------%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
}
- maxConcurrentOperationCount = 1;时运行结果:
![Paste_Image.png](https://img.haomeiwen.com/i2897594/77f7d614544ed644.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- maxConcurrentOperationCount = 2;时运行结果:
![Paste_Image.png](https://img.haomeiwen.com/i2897594/cae1b1df51cfeace.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
> - 结论:当最大并发数为1时,任务是按顺序串行执行的,当最大并发数大于1时,任务是按并发执行的。
###4. 操作依赖
> - NSOperation 和NSOperationQueue最吸引人的地方是它能添加操作之间的依赖关系。比如说A、B两个操作;B需A完成后才能进行操作,这是就需要让B依赖于A了。
具体代码如下:
/**
- B和A添加依赖
*/
-
(void)addDependcy
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-------%@",[NSThread currentThread]);
}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-------%@", [NSThread currentThread]);
}];//让op2依赖于op1,则先执行op1,在执行op2
[op2 addDependency:op1];[queue addOperation:op2];
[queue addOperation:op1];
}```
运行结果:
- 关键代码:
[op2 addDependency:op1];
//让op2依赖于op1,则先执行op1,在执行op2
5. 一些其他方法
- (void)cancel;
NSOperation 提供的方法,可取消单个任务
-
- (void)cancelAllOperations;
NSOperation提供的方法,可取消队列中的所有任务 -
- (void)setSuspended:(BOOL);
可设置任务的暂停和恢复,YES:暂停队列,NO:恢复队列 -
- (BOOL)isSuspended;
判断暂停状态
注意:
- 这里的暂停和取消并不代表可以将当前任务立即取消,而是当当前操作执行完毕之后不再执行新的操作。
- 暂停和取消的区别在于:暂停操作之后还可以恢复操作,继续向下执行;而取消任务之后,所有的操作就清空,无法再接着执行剩下的操作。