·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> app软件开发 >> IOS开发 >> iOSNSOperation非并发执行

iOSNSOperation非并发执行

作者:佚名      IOS开发编辑:admin      更新时间:2022-07-23
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任务会在下一节讲。