美文网首页iOS面试
iOS 多线程NSOperation

iOS 多线程NSOperation

作者: YourSummer | 来源:发表于2022-05-24 17:08 被阅读0次

一. 定义

NSOperation是苹果公司提供的一套多线程解决方案, 它是基于GCD 的更抽象的"面向对象"封装.

二. 对比GCD

  • 支持任务之间添加依赖关系, 控制执行顺序
  • 提供可选实现的任务完成block
  • 可使用KVO监听任务执行状态变化
  • 支持设置任务的优先级, 可控制任务的相对执行顺序
  • 提供取消操作

三. 任务

任务就是在线程中执行的代码, 在GCD中表现形式是block, 在NSOperationshi是在其子类NSInvocationOperation, NSBlockOperation, 自定义子类中执行任务代码.

四. 队列

NSOperationQueue表示队列, 用来存放任务的队列

  • NSOperation单独使用时是同步操作, 配合NSOperationQueue才能实现异步操作
  • GCD添加到队列中的任务遵守FIFO原则, 对于添加到NSOperationQueue中的任务, 首先根据任务之间的依赖关系决定任务的就绪状态, 进入就绪状态的任务由任务之间的相对优先级决定执行顺序
  • NSOperationQueue可以设置最大并发任务数量
  • NSOperationQueue 提供两种队列: 主队列和自定义队列,
    • 主队列在主线程运行
    • 自定义队列在后台执行

五. 基本使用

NSOperation是一个抽象类, 使用的时候必须子类化, 提供三种方式创建任务:

1. 使用子类NSInvocationOperation
NSInvocationOperation *invocationOpeartion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpeartion) object:nil];
    
// 如果没有添加到 NSOperationQueue, 则需要手动调用 start
[invocationOpeartion start];

/// 回调方法
- (void)invocationOpeartion {
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}

注意: NSInvocationOperation单独使用时, 并没有开启新的线程, 任务都是在当前线程中执行

2. 使用子类NSBlockOperation
  • 2.1 简单使用
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        // 任务代码
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];
    
[blockOperation start];

注意: NSBlockOperation单独使用时, 并没有开启新的线程, 任务都是在当前线程中执行

  • 2.2: NSBlockOperation类有一个实例方法addExecutionBlock, 这个方法可以添加一个代码块, 当执行NSBlockOperation对象 start 时, 该对象将其所有块提交给默认优先级的并发调度队列. 然后对象等待所有的块完成执行. 当最后一个块完成执行时, 操作对象将自己标记为已完成. 由于块操作本身在单独的线程上运行, 所以应用程序的其他线程可以在等待块操作完成的同时继续工作.
    需要说明的一点是, 如果添加的任务较多的话, 这些操作(包括blockOperationWithBlock中的操作)可能在不同的线程中并发执行, 这是由系统决定的
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任务代码
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];
    
[blockOperation addExecutionBlock:^{
    NSLog(@"addExecutionBlock1:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"addExecutionBlock2:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"addExecutionBlock3:%@", NSThread.currentThread);
}];
[blockOperation start];

在调用了addExecutionBlock方法添加了多组任务后,开启新的线程,任务是并发执行的,blockOperationWithBlock中的任务执行是否在当前的线程执行, 统一由系统调度.

3. 自定义继承自NSOperation的子类
@interface Operation : NSOperation

@end

@implementation Operation

-(void)main {
    if (!self.isCancelled) {
        for (int i = 0; i < 4; i++) {
            sleep(2);
            NSLog(@"%d==%@", i, NSThread.currentThread);
        }
    }
}

@end

// 使用:
Operation *op = [[Operation alloc] init];
[op start];

注意: 自定义的Operation并没有开启新的线程,任务的执行是在当前的线程中执行的。

六. 队列: NSOperationQueue

NSOperationQueue提供了主队列和自定义队列

  • 主队列: [NSOperationQueue mainQueue];, 凡是添加到主队列的NSOperation任务都会放到主线程执行
  • 自定义队列: [[NSOperationQueue alloc] init], 凡是添加到自定义队列的NSOperation任务都会放到子线程执行.
1.添加任务到队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 方式1: 
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任务代码
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];
[queue addOperation:blockOperation];

// 方式2: 
[queue addOperationWithBlock:^{
    // 任务代码
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];


// 注意: 不需要调用NSBlockOperationstart方法

2.添加多个操作到队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 操作1
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任务代码
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];

// 操作2
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];

// 方式1:
[queue addOperation:blockOperation];
[queue addOperation:invocationOperation];

// 方式2:
// waitUntilFinished参数,如果传YES,则表示会等待队列里面的任务执行完成后才会往下执行,也就是会阻塞线程
[queue addOperations:@[blockOperation, invocationOperation] waitUntilFinished:true];

/// 回调方法
- (void)invocationOperation {
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}

总结: waitUntilFinished参数,如果传YES,则表示会等待队列里面的任务执行完成后才会往下执行,也就是会阻塞线程

七. 同步&并发

NSOperationQueue有个一属性maxConcurrentOperationCount
maxConcurrentOperationCount 默认-1, 不做限制, 在子线程中执行任务
maxConcurrentOperationCount = 1, 队列中的任务会同步执行, 阻塞当前线程
maxConcurrentOperationCount > 1, 队列中的任务会并发执行, 开启子线程执行
maxConcurrentOperationCount值并不是表示并发执行的线程数量,而是在一个队列中能够同时执行的任务的数量。

八. NSOperation线程间的通讯, 子线程完成任务, 会主线程更新UI

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任务代码
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
    // 回主队列更新UI
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSLog(@"更新UI");
    }];
}];
[queue addOperation:blockOperation];

九. 任务的依赖关系

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    // 任务代码
    NSLog(@"1");
    
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"2");
}];

NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"3");
}];

// 1依赖于2和3, 意思是在2和3都执行完后才会执行1;
[operation1 addDependency:operation2];
[operation1 addDependency:operation3];
// 将1的优先级设置最高
operation1.queuePriority = NSOperationQueuePriorityHigh;

[queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];
//log: 2 3 1

总结:
queuePriority不能取代依赖关系
queuePriority属性只对同一个队列有效

十.设置完成回调

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    // 任务代码
    NSLog(@"1");
}];

// 设置任务1完成的回调block
[operation1 setCompletionBlock:^{
        NSLog(@"任务1完成了");
}];

十一. 监听状态

  • 使用KVO观察对operation状态的监听: isExcuting, isFinished, isCancelled.
  • isExcuting 正在执行任务
  • isFinished 任务完成
  • isCancelled 任务取消执行

相关文章

网友评论

    本文标题:iOS 多线程NSOperation

    本文链接:https://www.haomeiwen.com/subject/pyljprtx.html