NSOperation是常见的多线程方案,是对GCD的封装,采用OC方法,更加面向对象。
NSOperation的使用:配合使用NSOperation(任务)和NSOperationQueue(队列)也可以实现多线程。
1、将需要执行的操作封装到一个NSOperation对象中
2、然后将NSOperation对象添加到NSOperationQueue中
3、系统自动将NSOperationQueue中的NSOperation取出来
4、将取出来的NSOperation封装的操作放到一条新的线程中执行
以上4步不需要我们自己开启新的线程,系统会自动帮我们开启新的线程。
NSOperation是一个抽象的类,不具备封装操作的能力,必须使用其子类。
使用NSOperation子类的方式有三种:
NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现背部响应的方法
先看看NSInvocationOperation的使用:
- (void)invocationOperation
{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start]; //启动任务
}
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
打印的结果是在主线程的执行的,也就表示NSInvocationOperation实际上在不添加到队列里面,其效果等于performSelector,没啥意思。
但是再看看NSBlockOperation:
- (void)blockOperation
{
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];
}
由打印结果可以看出,NSBlockOperation最开始的任务是在主线程中执行的,再给NSBlockOperation添加的额外的任务是在子线程中执行的,程序自动帮助开启了子线程。
GCD队列和NSOperationQueue队列类型比较:
GCD的队列类型:
(1)并发队列:a.全局 b.自己创建的
(2)串行队列:a.主队列 b.自己创建的
NSOperationQueue队列类型:
(1)主队列[NSOperationQueue mainQueue],添加到朱队列中的任务,都会在主线程中执行。
(2)其他队列(串行、并发),添加到其他队列中的任务,都会自动放在子线程中执行。
NSOperation默认是同步执行的。
NSOperationQueue和NSOperation的基本使用:
- (void)operationTask
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *ope1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doTask) object:nil];
NSBlockOperation *ope2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task 2%@,%d",[NSThread currentThread],[NSThread isMainThread]);
}];
[ope2 addExecutionBlock:^{
NSLog(@"task 3%@,%d",[NSThread currentThread],[NSThread isMainThread]);
}];
[ope2 addExecutionBlock:^{
NSLog(@"task 4%@,%d",[NSThread currentThread],[NSThread isMainThread]);
}];
#pragma mark 将任务添加到队列中
[queue addOperation:ope1];
[queue addOperation:ope2];
#pragma mark 创建自定义任务对象
ZYOperation *ope3 = [[ZYOperation alloc] init];
#pragma mark 将自定义任务对象添加到队列中
[queue addOperation:ope3];
}
- (void)doTask
{
NSLog(@"task 1%@,%d",[NSThread currentThread],[NSThread isMainThread]);
}
#import "ZYOperation.h"
注意:ZYOperation是一个继承自NSOperation的类,重写这个类的- (void)main方法就可以在这个方法里面进行我们需要完成的任务了。自定义NSOperation任务有一个好处就是:一旦项目中有多个地方需要执行这个任务,我们就在自定义任务的- (void)main方法里面实现要完成的任务,然后在需要执行的地方ZYOperation *ope3 = [[ZYOperation alloc] init];初始化任务然后添加到队列中,就不需要多次实现这个任务内部的细节了。
@implementation ZYOperation
- (void)main
{
NSLog(@"task 5%@,%d",[NSThread currentThread],[NSThread isMainThread]);
}
@end
从上面的输出结果:凡是添加到NSOperationQueue中的任务,系统都自动帮我们开启了一个新的线程。
(void)setOperationType
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//设置最大并发操作数量,maxConcurrentOperationCount =1表示串行队列,>1代表并发队列
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
sleep(1); //模拟耗时操作
NSLog(@”task 1%@”,[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
sleep(1); //模拟耗时操作
NSLog(@”task 2%@”,[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
sleep(1); //模拟耗时操作
NSLog(@”task 3%@”,[NSThread currentThread]);
}];
}
可以看出一次只有一个线程在执行任务(但是我们无法控制开启的线程数目,比如task1和task2就是两个不同的线程中执行的,但不是同一时刻执行的)
如果将maxConcurrentOperationCount设置为3,输出结果如下:
可见此时处于并发队列。
将队列挂起(暂停):suspended属性
设置queue.suspended = YES;可以暂停队列,但是需要注意的是:正在进行的队列不可以被暂停,设置暂停后,程序要先将正在执行的任务执行完,未开始执行的任务将被暂停。
取消任务的执行:cancelAllOperations方法:
执行[queue cancelAllOperations]操作,可以调用所有队列的cancel方法。这样未被执行的任务将会被取消,正在执行的队列里面的任务会继续执行完。
- (void)main
{
for (int i = 0 ; i < 2000; i++) {
NSLog(@"task1:%d--->%@",i,[NSThread currentThread]);
}
if (self.cancelled) {
return;
}
for (int i = 0 ; i < 2000; i++) {
NSLog(@"task2:%d--->%@",i,[NSThread currentThread]);
}
if (self.cancelled) {
return;
}
for (int i = 0 ; i < 2000; i++) {
NSLog(@"task3:%d--->%@",i,[NSThread currentThread]);
}
if (self.cancelled) {
return;
}
}
有时候我们需要等某个任务完成以后再做另外一个任务,这样我们就可以给任务添加一个依赖,如下
- (void)operationDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
sleep(2);
NSLog(@"task 1%@",[NSThread currentThread]);
}];
NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task 2%@",[NSThread currentThread]);
}];
NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task 3%@",[NSThread currentThread]);
}];
//让任务3在任务1完成后再执行
[op3 addDependency:op1];
[queue addOperations:@[op1, op2, op3] waitUntilFinished:YES];
}
- (void)operationDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
sleep(2);
NSLog(@"task 1%@",[NSThread currentThread]);
}];
NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task 2%@",[NSThread currentThread]);
}];
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task 3%@",[NSThread currentThread]);
}];
NSOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
sleep(2);
NSLog(@"task 4%@",[NSThread currentThread]);
}];
//跨队列依赖(让队列1中的任务2等待队列2中的任务4完成后,再执行。用途很广泛)
[op2 addDependency:op4];
[queue2 addOperations:@[op3,op4] waitUntilFinished:YES];
[queue addOperations:@[op1, op2] waitUntilFinished:YES];
}
#pragma mark 监听任务执行转态 completionBlock
- (void)detectTaskFinish
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task 1%@",[NSThread currentThread]);
}];
[queue addOperation:op1];
#pragma mark 在completionBlock方法里面添加任务完成之后的事情,例如提醒等
op1.completionBlock = ^(){
NSLog(@"任务1执行完毕");
};
}