iOS多线程之NSOperation与NSOperationQueue的使用

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中的任务,系统都自动帮我们开启了一个新的线程。

pragma mark 设置最大并发操作数量maxConcurrentOperationCount

  • (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方法。这样未被执行的任务将会被取消,正在执行的队列里面的任务会继续执行完。

pragma mark 对于自定义的Operation,main中可能含有多个任务,建议每个操作执行完,执行一次 if (self.cancelled) return;操作,停止后面未完成的任务。

- (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;
    }
}

有时候我们需要等某个任务完成以后再做另外一个任务,这样我们就可以给任务添加一个依赖,如下

pragma mark 给任务添加依赖 addDependency:

- (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];

}

这里写图片描述

pragma mark 跨队列依赖任务 addDependency:

- (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

#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执行完毕");

    };
}

你可能感兴趣的:(iOS开发,多线程)