美文网首页iOS开发资料收集区
iOS 多线程之NSOpreation

iOS 多线程之NSOpreation

作者: 尤先森 | 来源:发表于2019-03-24 13:18 被阅读55次
图1.png

NSOpreation、NSOperationQueue简介

  • NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。
  • NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。
  • 虽然是基于GCD封装,但是比 GCD 更简单易用、代码可读性也更高。
  • GCD 中的一些概念同样适用于 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有类似的任务(操作)和队列(操作队列)的概念。
  • 在 NSOperation 中,由于NSOperation是一种抽象类,所以在使用过程中,我们需要用NSOperation的子类 NSInvocationOperation、NSBlockOperation。

啥是抽象类?
抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。
来自百度百科

NSOpreation、NSOperationQueue使用

创建队列NSOperationQueue
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
创建操作NSOpreation
  • NSInvocationOperation
    NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(InvocationOperationSelector) object:nil];

    [io start];    

    -(void)InvocationOperationSelector{
        NSLog(@"这是NSInvocationOperation执行的任务   %@",[NSThread currentThread]);
    }

输出结果


image.png

可知,在操作没有添加到队列中,操作直接start,任务是在主线程上执行,没有开启线程。

  • NSBlockOperation
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
    }];
    
    [bo start];

输出结果

image.png
从结果来看,跟NSInvocationOperation同理。尝试将操作添加到队列中看看输出结果。
  • NSBlockOperation添加额外的任务
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
    }];
    
    [bo addExecutionBlock:^{
        NSLog(@"这是让NSBlockOperation另外执行的任务 %@",[NSThread currentThread]);
    }];
    
    [bo addExecutionBlock:^{
        NSLog(@"这是让NSBlockOperation另外执行的任务2 %@",[NSThread currentThread]);
    }];
    
    [bo start];

输出结果

image.png
通过输出结果可知,给操作添加额外任务,并不会让这些任务按顺序执行。但是初始化的任务仍然在主线程执行,另外两个任务分别创建了线程。
所以NSBlockOperation是否创建线程,取决于当前需要执行的任务数。
那么,blockOperationWithBlock这里面的任务就一定会在主线程执行吗?我们给上面的代码加点戏,再看看打印结果。
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
        }
    }];
    
    [bo addExecutionBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"这是让NSBlockOperation另外执行的任务 %@",[NSThread currentThread]);
        }
    }];
    
    [bo addExecutionBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"这是让NSBlockOperation另外执行的任务2 %@",[NSThread currentThread]);
        }
    }];
    [bo start];

打印结果

image.png
细心的小朋友已经发现了,blockOperationWithBlock里面的任务,并没有在主线程执行啦。

所以,总结一下
1.NSBlockOperation是否创建线程,取决于当前需要执行的任务数。
2.blockOperationWithBlock里面的任务,并没有不一定主线程执行啦。

添加操作(NSOpreation)到队列(NSOperationQueue)
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(InvocationOperationSelector) object:nil];
   
     NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
        }
    }];
    [queue addOperation:io];
    [queue addOperation:bo];

输出结果


image.png

可见,两个操作都分别创建了number为3和4的线程,不再是主线程。

注意:start不能跟addOperation同时使用,否则会导致崩溃

image.png
添加、移除依赖

- (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作 op 的依赖。

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

    NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"这是bo1执行的任务 %@",[NSThread currentThread]);
    }];
    NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"这是bo2执行的任务 %@",[NSThread currentThread]);
    }];
    NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"这是bo3执行的任务 %@",[NSThread currentThread]);
    }];
    //bo1依赖于bo2
    [bo1 addDependency:bo2];
    //bo2依赖于bo3
    [bo2 addDependency:bo3];

    [queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES];
    
    NSLog(@"如果上面waitUntilFinished是YES,我就会最后执行");

输出结果

image.png
waitUntilFinished这里面传入的BOOL 值将会影响下面的代码执行顺序,如果是YES,将会形成一个类似同步函数、栅栏函数的同步功能。等队列中的任务中都执行结束后才执行下面的代码。
NSOperationQueue控制最大并发数(实现同步)
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    queue.maxConcurrentOperationCount = 1;
    
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i<5; i++) {
            NSLog(@"第%d次 执行任务 %@",i+1,[NSThread currentThread]);
        }
    }];
    
    [bo addExecutionBlock:^{
        for (int i = 0; i<5; i++) {
            NSLog(@"第%d次 执行额外任务 %@",i+1,[NSThread currentThread]);
        }
    }];
    
    [queue addOperation:bo];

先看输出结果

image.png
从输出结果可以看出来一些东西
1.任务并不是先执行任务后执行额外任务,而是相互穿插。
2.虽然跟理想中的结果不太一样,但是不论任务额外任务,却按照12345的顺序,老老实实的执行。
所以

这里 maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行任务的最大数。而且一个任务也并非只能在一个线程中运行。

线程间通讯
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(10, 100, 200, 10)];
    [self.view addSubview:label];
    
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSString * str = @"字符串赋值";
        NSLog(@"这是子线程%@",[NSThread currentThread]);
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            NSLog(@"回到主线程刷新UI%@",[NSThread currentThread]);
            label.text = str;
        }];
    }];
    [queue addOperation:bo];

打印以及显示结果


image.png

NSOperation、NSOperationQueue 常用属性和方法归纳

NSOperation 常用属性和方法

1.取消操作方法
- (void)cancel; 可取消操作,实质是标记 isCancelled 状态。

2.判断操作状态方法
- (BOOL)isFinished; 判断操作是否已经结束。
- (BOOL)isCancelled; 判断操作是否已经标记为取消。
- (BOOL)isExecuting; 判断操作是否正在在运行。
- (BOOL)isReady; 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。

3.操作同步
- (void)waitUntilFinished;阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
- (void)setCompletionBlock:(void (^)(void))block;completionBlock 会在当前操作执行完毕时执行 completionBlock。
- (void)addDependency:(NSOperation *)op;添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。

NSOperationQueue 常用属性和方法

1.取消/暂停/恢复操作
- (void)cancelAllOperations; 可以取消队列的所有操作。
- (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。

2.操作同步
- (void)waitUntilAllOperationsAreFinished;阻塞当前线程,直到队列中的操作全部执行完毕。

3.添加/获取操作
- (void)addOperationWithBlock:(void (^)(void))block;向队列中添加一个 NSBlockOperation 类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations;当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount;当前队列中的操作数。

4.获取队列
+ (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
+ (id)mainQueue;获取主队列。

部分摘取自西单_夜未央

相关文章

网友评论

    本文标题:iOS 多线程之NSOpreation

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