zoukankan      html  css  js  c++  java
  • iOS NSOperation 非并发执行

    NSOperation提供了一种面向对象的方法来封装任务。NSOperation可以单独执行,也可以放到NSOperationQueue中执行。
     
    NSOperation是虚基类不能直接使用,但Cocoa提供了两个简单的子类NSBlockOperation和NSInvocationOperation。NSBlockOperation是将任务封装到block对象中,NSInvocationOperation 是将任务封装到selector。
     
    NSBlockOperation 直接使用
    - (void)startOperation
    {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"NSBlockOperation Test");
        }];
     
        [operation start];
    }
     
    调用start方法开始执行任务,NSOperation的实例方法cancel可以取消正在执行的任务,这比GCD有优势(GCD中不提供取消任务的功能)。但是这里并不是我们想像的这么简单调用一个cancel方法就够了,看下面代码:
    - (void)startOperation
    {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            while (1) {
                NSLog(@"Test operation cancel funcation");
            }
        }];
       
        [operation start];
        [operation cancel];
    }
     
    start方法调用后while循环会一直执行,之后调用cancel方法,while循环会停止吗?答案是不会的,因为cancel方法就不会被执行,当前线程一直卡在block任务中。
    你可能会想我在start方法前设置0.01s延迟后调用实例的cancel方法呢?看下面代码:
    - (void)startOperation
    {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            while (1) {
                NSLog(@"Test operation cancel funcation");
            }
        }];
     
        self.operation = operation;
        [self performSelector:@selector(cancelOperation) withObject:nil afterDelay:0.01f];
     
        [operation start];
    }
     
    - (void)cancelOperation
    {
        [self.operation cancel];
    }
     
    实际运行发现cancelOperation方法根本就不会调用,这是为什么呢?
    NSOperation本身并不提供多线程的能力,任务是在当前线程中异步执行,任务执行完成后才执行后面的代码。cancel方法写在当前线程,而当前线程一直卡在while循环里,所以cancel方法根本就不会被调用。
     
    既然在同一线程中不能取消死循环的任务,那么,将任务放到后台,在主线程中取消呢?NSOperationQueue提供多线程的能力,将NSOperation任务放到queue中执行。看下面代码:
    - (void)startOperation
    {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            while (1) {
                NSLog(@"Test operation cancel funcation");
            }
        }];

        self.operation = operation;

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:operation];
    //    [operation cancel]; 
       
        [self performSelector:@selector(cancelOperation) withObject:nil afterDelay:0.01f];
    }
     
    这里的cancelOperation方法和上面一样省略了,operation任务被添加到NSOperationQueue中在下一个Runloop会被执行,如果紧跟添加后取消,任务就不会被执行。所以我们放到延迟方法中取消operation。这样总能退出while循环了吧!测试发现while循环依然在执行,这又是什么原因了?
     
    在苹果官方文档上讲解NSOperation有这么一段话:
     If an operation were terminated outright, there might not be a way to reclaim resources that had been allocated. As a result, operation objects are expected to check for cancellation events and to exit gracefully when they occur in the middle of the operation.
    大致意思是任务被中断了,但分配的内存资源有可能回收不了,所以在执行任务前要检查任务是否被取消了。还有保证任务被取消后释放分配的内存,这点在后面的实现NSOperation子类中要特别注意。
     
    下面这样才是正确的
    - (void)startOperation
    {
        __weak ViewController *wself = self;
       
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            while (wself.operation.isCancelled == NO) {
                NSLog(@"Test operation cancel funcation");
            }
        }];

        self.operation = operation;

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:operation];
     
        [self performSelector:@selector(cancelOperation) withObject:nil afterDelay:0.01f];
    }
     
    上面使用的都是系统提供的NSOperation的子类,我们也可以自己定义新的子类。NSOperation的任务默认是非并发执行的,只读属性 BOOL concurrent(iOS7之后使用 BOOL asynchronous)默认返回NO。
     
     
    将NSOperationQueue之前先弄明白几个概念:线程、同步、异步、并发
    线程是程序执行的最小单元,是进程中的一个实体,是被系统独立调度和分派的基本单位,同一进程中的多个线程可以并发执行。
    线程同步是多个线程发生竞争资源,需要依次访问,线程异步是多个线程可以同时对同一资源进行访问
    执行同步是等待任务完成才能执行后面的代码,执行异步是异步调用发出后,接着执行后面的代码,实际执行调用的过程在后面完成,像performSelector调用。
    并发执行是指不需要等待任务执行完也能执行后面的代码。
     
    上面我用到了NSOperationQueue,处于cocoa最上层处理多线程队列。NSOperationQueue会给加入的每个NSOperation任务开启一个新的线程,当任务执行完成后销毁其线程。多个任务是异步执行的,既为异步队列。但可以设置NSOperationQueue的最大同时执行的任务数为1(maxConcurrentOperationCount = 1)来实现同步队列。非并发的任务添加到NSOperationQueue队列中也实现了异步执行。因此如果你需要将NSOperation任务添加到NSOperationQueue队列中,那就不需要实现NSOperation的并发任务。
     
    关于自定义NSOperation子类和实现concurrent任务会在下一节讲。
     
  • 相关阅读:
    Shell脚本中cd命令使用
    OpenStack 的Nova组件详解
    Linux 查看网络连接状态
    Linux 怎么查看服务的启动进程所占用的目录
    邮政短信 运营商常见错误
    Linux 怎么把自己写的脚本添加到服务里面,即可以使用service命令来调用
    Linux 命令行生成随机密码的十种方法
    Linux rpm安装问题解决
    Codeforces 1082G(最大权闭合子图)
    Codeforces 1105D (BFS)
  • 原文地址:https://www.cnblogs.com/shuleihen/p/4368897.html
Copyright © 2011-2022 走看看