iOS - 操作队列

操作队列(NSOperationQueue)是一个可以方便实现后台操作的工作队列,它是在GCD的基础上使用Cocoa抽象出来的一个队列模型。操作队列和GCD一样既可以在主线程运行,做一些UI刷新操作,也可以在子线程运行。

操作队列里的工作被封装在一个NSOperation对象里,NSOperation本身是抽象对象不能直接使用,需要创建它的子类或者使用系统提供的两个子类 NSBlockOperation 或 NSInvocationOperation。

NSOperation

NSOperation 将工作单独封装成一个单元,并提供了一些线程安全的特性,比如状态(state),优先级(priority),依赖(dependencies)以及取消(cancellation)等。

1,状态(state):

状态(state)描述了一个 operation 的执行过程:

isReady -> isExecuting -> isFinished

1)isReady:isReady属性与dependencies有关,当operation依赖的工作完成时,会获得一个isReady键值的KVO通知,然后isReady属性修改为YES,标识该工作已经准备好。

2)isExecuting:如果操作队列正在执行该operation,operation的isExecuting返回YES。

3)isFinished: 任务成功的完成了或者中途被Cancel掉,该值返回YES。NSOperationQueue只会把isFinished为YES的operation踢出队列,isFinished为NO的永远不会被移除,所以实现时一定要保证其正确性,避免死锁的情况发生。

2,取消(cancellation):

NSOperation里的工作是可以取消的,取消一个operation可以是显式的调用cancel方法,也可以是operation依赖的其他operation执行失败。

和state类似,当NSOperation被取消,是通过isCancelled键值的KVO来获得。当NSOperation的子类覆写cancel方法时,注意清理掉内部分配的资源。特别注意的是,这时isCancelled和isFinished的值都变为了YES,isExecuting为值变为NO

3,优先级(priority):

所有的operation在NSOperationQueue中未必都是一样的重要,设置queuePriority属性就可以提升和降低operation的优先级,queuePriority属性可选的值如下:

  • NSOperationQueuePriorityVeryHigh
  • NSOperationQueuePriorityHigh
  • NSOperationQueuePriorityNormal
  • NSOperationQueuePriorityLow
  • NSOperationQueuePriorityVeryLow

4,依赖(Dependencies):

如果你从服务器上下载一张图片完成后进行压缩,你可能会想把下载图片作为一个operation,压缩作为另外一个。一个图片在从服务器上下载下来之前是没有办法压缩的,于是我们说压缩图片的operation依赖从服务器上下载图片的operation,后者必须先完成,前者才能开始执行。通过代码来说就是:

[resizingOperation addDependency:networkingOperation];
[operationQueue addOperation:networkingOperation];
[operationQueue addOperation:resizingOperation];

除非一个操作的依赖的isFinished返回YES,不然这个操作不会开始。要记住添加到队列里的所有的operation的依赖关系,并避免循环依赖,比如A依赖B,B依赖A,这样会产生死锁。

自定义NSOperation子类

对于非并发的工作,你只需要实现NSOperation子类里的main方法:

@implementation YourOperation

-(void)main
{
    if(self.isCancelled == NO)
    {
        //执行相应的操作
    }
}

@end

如果要支持并发工作,那么NSOperation子类需要至少重写这四个方法:

  • start
  • isConcurrent
  • isExecuting
  • isFinished

start方法是工作的入口,通常是你用来设置线程或者其他执行工作任务需要的运行环境的,注意不要调用[super start];isConcurrent是标识这个Operation是否是并发执行的,这里曾经是个坑,如果你没有实现isConcurrent,默认是返回NO,那么你的NSOperation就不是并发执行而是串行执行的,不过在iOS5.0和OS X10.6之后,已经会默认忽略这个返回值,最终和Queue的maxConcurrentOperationCount最大并发操作值相关;isExecuting和isFinished是用来报告当前的工作执行状态情况的,注意必须是线程访问安全的。

注意你的实现要发出合适的KVO通知,因为如果你的NSOperation实现需要用到工作依赖从属特性,而你的实现里没有发出合适的“isFinished”KVO通知,依赖你的NSOperation就无法正常执行。NSOperation支持KVO的属性有:

  • isCancelled
  • isConcurrent
  • isExecuting
  • isFinished
  • isReady
  • dependencies
  • queuePriority
  • completionBlock

当然也不是说所有的KVO通知都需要自己去实现,例如通常你用不到addObserver到你工作的“isCancelled”属性,你只需要直接调用cancel方法就可以取消这个工作任务。

NSInvocationOperation & NSBlockOperation

除此之外,对于一些不复杂的工作,可以由官方提供的NSOperation两个子类 NSInvocationOperation 和 NSBlockOperation 来实现。

NSInvocationOperation:

NSInvocationOperation* operation = [[NSInvocationOperation alloc]
                                        initWithTarget:self
                                        selector:@selector(doSomeWork:)
                                        object:nil];

NSBlockOperation:

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        // Do some work.
    }];

NSOperationQueue

创建一个子线程队列,并将operation添加至队列里:

NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];

或者,直接使用主线程队列:

NSOperationQueue* operationQueue = [NSOperationQueue mainQueue];

设置并发工作的数量:

operationQueue.maxConcurrentOperationCount = 1;

取消所有队列工作:

[operationQueue cancelAllOperations];

参见

Copyright © 2017 - sbxfc - Powered by Octopress