上一篇写了 GCD 的使用,接下来就了解一下 NSOperation ,NSOperation是苹果对 GCD 的 OC 版的一个封装,可是相对于GCD来讲可控性更强,而且能够加入操做依赖。安全
NSOperation是一个抽象的基类,表示一个独立的计算单元,能够为子类提供有用且线程安全的创建状态,优先级,依赖和取消等操做。系统已经给咱们封装了NSBlockOperation和NSInvocationOperation这两个实体类。使用起来也很是简单,不过咱们更多的使用是本身继承并定制本身的操做。网络
- (void)start; - (void)main; @property (readonly, getter=isCancelled) BOOL cancelled; - (void)cancel; @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below @property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0); @property (readonly, getter=isReady) BOOL ready; - (void)addDependency:(NSOperation *)op; - (void)removeDependency:(NSOperation *)op; @property (readonly, copy) NSArray *dependencies; typedef NS_ENUM(NSInteger, NSOperationQueuePriority) { NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 }; @property NSOperationQueuePriority queuePriority; @property (copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0); - (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0); @property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0); @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0); @property (copy) NSString *name NS_AVAILABLE(10_10, 8_0);
NSOperation提供了ready
cancelled
executing
finished
这几个状态变化,咱们的开发也是必须处理本身关心的其中的状态。这些状态都是基于keypath的KVO通知决定,因此在你手动改变本身关心的状态时,请别忘了手动发送通知。这里面每一个属性都是相互独立的,同时只可能有一个状态是YES。finished
这个状态在操做完成后请及时设置为YES,由于NSOperationQueue所管理的队列中,只有isFinished为YES时才将其移除队列,这点在内存管理和避免死锁很关键。多线程
NSOperation中咱们能够为操做分解为若干个小的任务,经过添加他们之间的依赖关系进行操做,这点在设计上是颇有意义的。好比咱们最经常使用的图片异步加载,第一步咱们是去经过网络进行加载,第二步咱们可能须要对图片进行下处理(调整大小或者压缩保存)。咱们能够直接调用- (void)addDependency:(NSOperation*)op;
这个方法添加依赖:异步
[imgRsizingOperation addDependency:networkOperation]; [operationQueue addOperation:networkOperation]; [operationQueue addOperation:imgRsizingOperation];
这点咱们必需要注意的是不能添加相互依赖,像A依赖B,B依赖A,这样会致使死锁!还有一点必需要注意的时候,在每一个操做完成时,请将isFinished
设置为YES,否则后续的操做是不会开始执行的。async
执行一个operation有两种方法,第一种是本身手动的调用start
这个方法,这种方法调用会在当前调用的线程进行同步执行,因此在主线程里面本身必定要当心的调用,否则就会把主线程给卡死,还不如直接用GCD呢。第二种是将operation添加到operationQueue中去,这个也是咱们用得最多的也是提倡的方法。NSOperationQueue会在咱们添加进去operation的时候尽快进行执行。固然若是NSOperationQueue的maxConcurrentOperationCount
若是设置为1的话,进至关于FIFO了。ide
队列是怎么调用咱们的执行的操做的呢?若是你只是想弄一个同步的方法,那很简单,你只要重写main
这个函数,在里面添加你要的操做。若是想定义异步的方法的话就重写start
方法。在你添加进operationQueue中的时候系统将自动调用你这个start方法,这时将再也不调用main里面的方法。函数
NSOperation容许咱们调用-(void)cancel
取消一个操做的执行。固然,这个操做并非咱们所想象的取消。这个取消的步骤是这样的,若是这个操做在队列中没有执行,那么这个时候取消并将状态finished
设置为YES,那么这个时候的取消就是直接取消了。若是这个操做已经在执行了,那么咱们只能等其操做完成。当咱们调用cancel方法的时候,他只是将isCancelled
设置为YES。因此,在咱们的操做中,咱们应该在每一个操做开始前,或者在每一个有意义的实际操做完成后,先检查下这个属性是否是已经设置为YES。若是是YES,则后面操做均可以不用在执行了。ui
iOS4后添加了这个block,在这个操做完成时,将会调用这个block一次,这样也很是方便的让咱们对view进行更新或者添加本身的业务逻辑代码。atom
operationQueue有maxConcurrentOperationCount
设置,当队列中operation不少时而你想让后续的操做提早被执行的时候,你能够为你的operation设置优先级spa
NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8
最后咱们看看一个简单的小示例,在.m文件里面咱们将重写finished
executing
两个属性。咱们重写set方法,手动发送keyPath的KVO通知。在start
函数中,咱们首先判断是否已经取消,若是取消的话,咱们将直接return,并将finished
设置为YES。若是没有取消操做,咱们将_executing
设置为YES,表示当前operation正在执行,继续执行咱们的逻辑代码。在执行完咱们的代码后,别忘了设置operation的状态,将_executing
设置为NO,并将finished
设置为YES,这样咱们就已经很简单的完成了咱们的多线程操做任务。
@interface TestOperation () @property (nonatomic, assign) BOOL finished; @property (nonatomic, assign) BOOL executing; @end @implementation TestOperation @synthesize finished = _finished; @synthesize executing = _executing; - (void)start { if ([self isCancelled]) { _finished = YES; return; } else { _executing = YES; //start your task; //end your task _executing = NO; _finished = YES; } } - (void)setFinished:(BOOL)finished { [self willChangeValueForKey:@"isFinished"]; _finished = finished; [self didChangeValueForKey:@"isFinished"]; } - (void)setExecuting:(BOOL)executing { [self willChangeValueForKey:@"isExecuting"]; _executing = executing; [self didChangeValueForKey:@"isExecuting"]; }