iOS收藏iOS

iOS多线程之NSOperation、NSOperationQu

2019-03-04  本文已影响163人  云霄_云霄

1.什么是NSOperation:

NSOperation和NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation和NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

2.NSOperation的子类

NSOperation是一个抽象类,并不具备封装操作的能力,必须使用它的子类,使用NSOperation的子类的方式有三种:

  1. NSInvocationOperation以函数的方式封装任务
  2. NSBlockOperation以Block块的方式封装任务
  3. 自定义子类继承NSOperation,实现内部相应的方法。

3.NSOperation、NSOperationQueue 操作和操作队列

既然是基于 GCD 的更高一层的封装。那么,GCD 中的一些概念同样适用于 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有类似的任务(操作)和队列(操作队列)的概念。

3.1 操作(Operation):
3.2 操作队列(Operation Queues):

4.NSOperation和NSOperationQueue实现多线程的具体步骤

  1. 先将需要执行的操作封装到一个NSOperation中
  2. 然后将NSOperation对象添加到NSOperationQueue中
  3. 系统会自动将NSOperationQueue中的NSOperation取出来
  4. 将取出来的NSOperation封装的操作放到一条新线程中执行
//简单添加任务
/*
 输入结果
 2019-03-04 15:44:05.880348+0800 SmallProgram[4075:178369] 1---<NSThread: 0x600000ece7c0>{number = 1, name = main}
 2019-03-04 15:44:05.880541+0800 SmallProgram[4075:178369] 2---<NSThread: 0x600000ece7c0>{number = 1, name = main}
 2019-03-04 15:44:05.880711+0800 SmallProgram[4075:178369] 3---<NSThread: 0x600000ece7c0>{number = 1, name = main}
 2019-03-04 15:44:05.880736+0800 SmallProgram[4075:178504] 4---<NSThread: 0x600000e68b00>{number = 7, name = (null)}
 2019-03-04 15:44:05.880736+0800 SmallProgram[4075:178482] 5---<NSThread: 0x600000e68140>{number = 8, name = (null)}
 */
-(void)blockOperation{
    //01 --封装操作对象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任务01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任务02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    //追加任务
    //当一个操作中的任务数量大于1的时候,就会开启子线程和当前线程一起执行任务
    [op3 addExecutionBlock:^{
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    //02--执行操作
    [op1 start];
    [op2 start];
    [op3 start];
    
}

5.操作队列NSOperationQueue的基本使用

-(void)blockOperationQueue{
    
    //01 --创建队列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //02 --封装操作对象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任务01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任务02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    //03 --把操作添加到队列中
    [queue addOperation:op1]; //该方法内部会自动调用start方法执行任务
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    /*
    //简便方法:该方法n首先会吧block中任务封装成一个操作,然后把该操作直接添加到队列中
    [queue addOperationWithBlock:^{
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    */
}

6.设置maxConcurrentOperationCount来改为串行队列

-(void)changeSerialQueue{
    
    //01 --创建队列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //02 --封装操作对象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任务01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任务02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op5 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    //设置最大并发数--同一时间最多有多少调线程执行
    //maxConcurrentOperationCount ==0 不执行任务
    //maxConcurrentOperationCount默认为-1,指一个最大的值
    queue.maxConcurrentOperationCount = 1;
    //03 --把操作添加到队列中
    [queue addOperation:op1]; //该方法内部会自动调用start方法执行任务
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];

}

7.操作队列的暂停、回复和取消功能

    //暂停
    //只能暂停当前操作后面的操作,当前操作不可分割必须执行完毕
    [queue setSuspended:YES];
    //恢复
    [queue setSuspended:NO];
    //取消
    //智能取消队列中处理等待状态的操作
    [queue cancelAllOperations];

8.自定义操作

//.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MYNSOperation : NSOperation

@end

NS_ASSUME_NONNULL_END

//.m文件
#import "MYNSOperation.h"

@implementation MYNSOperation

//重写main方法来告诉自定义的操作任务是什么
//好处及其使用:比如工程中多次利用到图片下载,就可以将下载功能放到main方法中,以后调用就直接alloc就可以了
-(void)main{
    if(self.isCancelled){
        return;
    }
    //执行的任务
    for(int i=0;i<5;i++){
        NSLog(@"%d--%@",i,[NSThread currentThread]);
    }
}
@end

//使用方法
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
MYNSOperation * op = [[MYNSOperation alloc]init];
[queue addOperation:op]; //内容会调用start--->main

9.操作的依赖和监听

/*
 输出结果:
 2019-03-04 17:14:04.382909+0800 SmallProgram[5396:240448] 4---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
 2019-03-04 17:14:04.383237+0800 SmallProgram[5396:240448] 电影已经下载好了,可以观看了
 2019-03-04 17:14:04.383261+0800 SmallProgram[5396:240470] 3---<NSThread: 0x600001e59b80>{number = 9, name = (null)}
 2019-03-04 17:14:04.383440+0800 SmallProgram[5396:240470] 2---<NSThread: 0x600001e59b80>{number = 9, name = (null)}
 2019-03-04 17:14:04.383712+0800 SmallProgram[5396:240448] 1---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
 2019-03-04 17:14:04.383955+0800 SmallProgram[5396:240448] 5---<NSThread: 0x600001e9c000>{number = 3, name = (null)}
 */
-(void)relyOn{
    
    //01 --创建队列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    NSOperationQueue * queue2 = [[NSOperationQueue alloc] init];
    //02 --封装操作对象
    NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
        //任务01
        NSLog(@"1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
        //任务02
        NSLog(@"2---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"3---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    NSBlockOperation * op5 = [NSBlockOperation blockOperationWithBlock:^{
        //任务03
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    
    //监听任务执行完毕
    op4.completionBlock = ^{
        NSLog(@"电影已经下载好了,可以观看了");
    };
    
    //03设置依赖4-->3-->2-->1-->5
    //警告:不能设置循环依赖,否则死锁
    [op5 addDependency:op1];
    [op1 addDependency:op2];
    [op2 addDependency:op3];
    [op3 addDependency:op4];
    
    //04 --把操作添加到队列中
    [queue addOperation:op1]; //该方法内部会自动调用start方法执行任务
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue2 addOperation:op5];
}

10.操作队列实现线程间通信(模仿子线程下载图片,然后主线程刷新UI)

/*
 输出结果:
 下载中--<NSThread: 0x600001328bc0>{number = 8, name = (null)}
 2019-03-04 17:24:28.905424+0800 SmallProgram[5558:245680] 刷新UI--<NSThread: 0x600001389840>{number = 1, name = main}
 */
-(void)download{
    //01 --创建队列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    //02 --封装操作对象
    NSBlockOperation * download = [NSBlockOperation blockOperationWithBlock:^{
        //01--下载
        NSLog(@"下载中--%@",[NSThread currentThread]);
        //02--UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"刷新UI--%@",[NSThread currentThread]);
        }];
        
    }];
    [queue addOperation:download];
}

11.NSOperation、NSOperationQueue 线程安全(模仿卖火车票)

- (void)ticketStatusSave {
    
    self.ticketCount = 100; //模拟总共100张票
    self.lock = [[NSLock alloc] init]; //初始化锁
    
    // 01.创建 queue1,模拟窗口1
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    queue1.maxConcurrentOperationCount = 1;
    
    // 02.创建 queue2,模拟窗口2
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    queue2.maxConcurrentOperationCount = 1;
    
    // 03.添加到队列,然后开始卖票
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketSafe];
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketSafe];
    }];
    
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
}

/**
 * 售卖火车票(线程安全)
 */
- (void)saleTicketSafe {
    while (1) {
        
        // 加锁
        [self.lock lock];
        
        if (self.ticketCount > 0) {
            //如果还有票,继续售卖
            self.ticketCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%zd 窗口:%@", self.ticketCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        }
        // 解锁
        [self.lock unlock];
        if (self.ticketCount <= 0) {
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

12.GCD和NSOperation的比较

12.1 GCD:
12.2 NSOperation和NSOperationQueue的优势:

结尾

本文参考:
iOS 多线程:『NSOperation、NSOperationQueue』详尽总结https://www.jianshu.com/p/4b1d77054b35

上一篇 下一篇

猜你喜欢

热点阅读