美文网首页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