iOS 并发编程(1)

iOS Concurrency Programming Guide 程序员

iOS 和 Mac OS 传统的并发编程模型是线程,不过线程模型伸缩性不强,并且编写正确的线程代码也不容易。Mac OS 和 iOS 采起 asynchronous design approach 来解决并发的问题。 编程

引入的异步技术有两个: 安全

Grand Central Dispatch:系统管理线程,你不须要编写线程代码。只需定义想要执行的任务,而后添加到适当的dispatch queue。Grand Central Dispatch会负责建立线程和调度你的任务。系统直接提供线程管理,比应用实现更加高效。 数据结构

Operation Queue:Objective-C对象,相似于dispatch queue。你定义想要执行的任务,并添加任务到operation queue,后者负责调度和执行这些任务。和Grand Central Dispatch同样,Operation Queue也管理了线程,更加高效。 并发

Dispatch Queue app

基于C的执行自定义任务机制。dispatch queue按先进先出的顺序,串行或并发地执行任务。serial dispaptch queue一次只能执行一个任务,直接当前任务完成才开始出列并启动下一个任务。而concurrent dispatch queue则尽量多地启动任务并发执行。 iphone

优势: 异步

直观而简单的编程接口 async

提供自动和总体的线程池管理 ide

提供汇编级调优的速度

更加高效地使用内存

不会trap内核under load

异步分派任务到dispatch queue不会致使queue死锁

伸缩性强

serial dispatch queue比锁和其它同步原语更加高效

Dispatch Sources

Dispatch Sources 是基于C的系统事件异步处理机制。一个Dispatch Source封装了一个特定类型的系统事件,当事件发生时提交一个特定的block对象或函数到dispatch queue。你可使用Dispatch Sources监控如下类型的系统事件:

定时器

信号处理器

描述符相关的事件

进程相关的事件

Mach port事件

你触发的自定义事件

Operation Queues

Operation Queues是Cocoa版本的并发dispatch queue,由 NSOperationQueue 类实现。dispatch queue老是按先进先出的顺序执行任务,而 Operation Queues 在肯定任务执行顺序时,还会考虑其它因素。最主要的一个因素是指定任务是否依赖于另外一个任务的完成。你在定义任务时配置依赖性,从而建立复杂的任务执行顺序图

提交到Operation Queues的任务必须是 NSOperation 对象,operation object封装了你要执行的工做,以及所需的全部数据。因为 NSOperation 是一个抽象基类,一般你须要定义自定义子类来执行任务。不过Foundation framework自带了一些具体子类,你能够建立并执行相关的任务。

Operation objects会产生key-value observing(KVO)通知,对于监控任务的进程很是有用。虽然operation queue老是并发地执行任务,你可使用依赖,在须要时确保顺序执行

异步设计技术

经过确保主线程自由响应用户事件,并发能够很好地提升应用的响应性。经过将工做分配到多核,还能提升应用处理的性能。可是并发也带来必定的额外开销,而且使代码更加复杂,更难编写和调试代码。

所以在应用设计阶段,就应该考虑并发,设计应用须要执行的任务,及任务所需的数据结构。

Operation Queues

基于Objective-C,所以基于Cocoa的应用一般会使用Operation Queues

Operation Objects

operation object 是 NSOperation 类的实例,封装了应用须要执行的任务,和执行任务所需的数据。NSOperation 自己是抽象基类,咱们必须实现子类。Foundation framework提供了两个具体子类,你能够直接使用:

描述
NSInvocationOperation 能够直接使用的类,基于应用的一个对象和selector来建立operation object。若是你已经有现有的方法来执行须要的任务,就可使用这个类。
NSBlockOperation 能够直接使用的类,用来并发地执行一个或多个block对象。operation object使用“组”的语义来执行多个block对象,全部相关的block都执行完成以后,operation object才算完成。
NSOperation 基类,用来自定义子类operation object。继承NSOperation能够彻底控制operation object的实现,包括修改操做执行和状态报告的方式。

全部operation objects都支持如下关键特性:

支持创建基于图的operation objects依赖。能够阻止某个operation运行,直到它依赖的全部operation都已经完成。

支持可选的completion block,在operation的主任务完成后调用。

支持应用使用KVO通知来监控operation的执行状态。

支持operation优先级,从而影响相对的执行顺序

支持取消,容许你停止正在执行的任务

并发 VS 非并发Operations

一般咱们经过将operation添加到operation queue中来执行该操做。可是咱们也能够手动调用start方法来执行一个operation对象,这样作不保证operation会并发执行。NSOperation类对象的 isConcurrent 方法告诉你这个operation相对于调用start方法的线程,是同步仍是异步执行的。isConcurrent 方法默认返回NO,表示operation与调用线程同步执行。

若是你须要实现并发operation,也就是相对调用线程异步执行的操做。你必须添加额外的代码,来异步地启动操做。例如生成一个线程、调用异步系统函数,以确保start方法启动任务,并当即返回。

多数开发者历来都不须要实现并发operation对象,咱们只须要将operations添加到operation queue。当你提交非并发operation到operation queue时,queue会建立线程来运行你的操做,所以也能达到异步执行的目的。只有你不但愿使用operation queue来执行operation时,才须要定义并发operations。

建立一个 NSInvocationOperation 对象

若是已经现有一个方法,须要并发地执行,就能够直接建立 NSInvocationOperation 对象,而不须要本身继承 NSOperation。

 
  1. @implementation MyCustomClass 
  2. - (NSOperation*)taskWithData:(id)data { 
  3. NSInvocationOperation* theOp = [[[NSInvocationOperation alloc] initWithTarget:self 
  4. selector:@selector(myTaskMethod:) object:data] autorelease]; 
  5.  
  6. return theOp; 
  7.  
  8. // This is the method that does the actual work of the task. 
  9. - (void)myTaskMethod:(id)data { 
  10. // Perform the task. 
  11. @end 

建立一个 NSBlockOperation 对象

NSBlockOperation 对象用于封装一个或多个block对象,通常建立时会添加至少一个block,而后再根据须要添加更多的block。当 NSBlockOperation 对象执行时,会把全部block提交到默认优先级的并发dispatch queue。而后 NSBlockOperation 对象等待全部block完成执行,最后标记本身已完成。所以可使用block operation来跟踪一组执行中的block,有点相似于thread join等待多个线程的结果。区别在于block operation自己也运行在一个单独的线程,应用的其它线程在等待block operation完成时能够继续工做。

 
  1. NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{ 
  2. NSLog(@"Beginning operation.\n"); 
  3. // Do some work. 
  4. }]; 

使用 addExecutionBlock: 能够添加更多block到这个block operation对象。若是须要顺序地执行block,你必须直接提交到所需的dispatch queue。

自定义Operation对象

若是block operation和invocation operation对象不符合应用的需求,你能够直接继承 NSOperation,并添加任何你想要的行为。NSOperation 类提供通用的子类继承点,并且实现了许多重要的基础设施来处理依赖和KVO通知。继承所需的工做量主要取决于你要实现非并发仍是并发的operation。

定义非并发operation要简单许多,只须要执行主任务,并正确地响应取消事件;NSOperation 处理了其它全部事情。对于并发operation,你必须替换某些现有的基础设施代码。

执行主任务

每一个operation对象至少须要实现如下方法:

自定义initialization方法:初始化,将operation 对象设置为已知状态

自定义main方法:执行你的任务

你也能够选择性地实现如下方法:

main方法中须要调用的其它自定义方法

Accessor方法:设置和访问operation对象的数据

dealloc方法:清理operation对象分配的全部内存

NSCoding 协议的方法:容许operation对象archive和unarchive

 
  1. @interface MyNonConcurrentOperation : NSOperation { 
  2. id myData; 
  3. -(id)initWithData:(id)data; 
  4. @end 
  5.  
  6. @implementation MyNonConcurrentOperation 
  7. - (id)initWithData:(id)data { 
  8. if (self = [super init]) 
  9. myData = [data retain]; 
  10. return self; 
  11.  
  12. - (void)dealloc { 
  13. [myData release]; 
  14. [super dealloc]; 
  15.  
  16. -(void)main { 
  17. @try { 
  18. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
  19. // Do some work on myData and report the results. 
  20. [pool release]; 
  21. @catch(...) { 
  22. // Do not rethrow exceptions. 
  23. @end 

响应取消事件

operation开始执行以后,会一直执行任务直到完成,或者显式地取消操做。取消可能在任什么时候候发生,甚至在operation执行以前。尽管 NSOperation 提供了一个方法,让应用取消一个操做,可是识别出取消事件则是你的事情。若是operation直接终止,可能没法回收全部已分配的内存或资源。所以operation对象须要检测取消事件,并优雅地退出执行。

operation 对象按期地调用 isCancelled 方法,若是返回YES(表示已取消),则当即退出执行。无论是自定义 NSOperation 子类,仍是使用系统提供的两个具体子类,都须要支持取消。isCancelled方法自己很是轻量,能够频繁地调用而不产生大的性能损失。如下地方可能须要调用isCancelled:

在执行任何实际的工做以前

在循环的每次迭代过程当中,若是每一个迭代相对较长可能须要调用屡次

代码中相对比较容易停止操做的任何地方

 
  1. - (void)main { 
  2. @try { 
  3. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
  4. BOOL isDone = NO; 
  5.  
  6. while (![self isCancelled] && !isDone) { 
  7. // Do some work and set isDone to YES when finished 
  8. [pool release]; 
  9. @catch(...) { 
  10. // Do not rethrow exceptions. 

注意你的代码还须要完成全部相关的资源清理工做

为并发执行配置operations

Operation对象默认按同步方式执行,也就是在调用start方法的那个线程中直接执行。因为operation queue为非并发operation提供了线程支持,对应用来讲,多数operations仍然是异步执行的。可是若是你但愿手工执行operations,并且仍然但愿可以异步执行操做,你就必须采起适当的措施,经过定义operation对象为并发操做来实现。

方法 描述
start (必须)全部并发操做都必须覆盖这个方法,以自定义的实现替换默认行为。手动执行一个操做时,你会调用start方法。所以你对这个方法的实现是操做的起点,设置一个线程或其它执行环境,来执行你的任务。你的实如今任什么时候候都绝对不能调用super。
main (可选)这个方法一般用来实现operation对象相关联的任务。尽管你能够在start方法中执行任务,使用main来实现任务可让你的代码更加清晰地分离设置和任务代码
isExecuting
isFinished
(必须)并发操做负责设置本身的执行环境,并向外部client报告执行环境的状态。所以并发操做必须维护某些状态信息,以知道是否正在执行任务,是否已经完成任务。使用这两个方法报告本身的状态。
这两个方法的实现必须可以在其它多个线程中同时调用。另外这些方法报告的状态变化时,还须要为相应的key path产生适当的KVO通知。
isConcurrent (必须)标识一个操做是否并发operation,覆盖这个方法并返回YES
 
  1. @interface MyOperation : NSOperation { 
  2. BOOL        executing; 
  3. BOOL        finished; 
  4. - (void)completeOperation; 
  5. @end 
  6.  
  7. @implementation MyOperation 
  8. - (id)init { 
  9. self = [super init]; 
  10. if (self) { 
  11. executing = NO; 
  12. finished = NO; 
  13. return self; 
  14.  
  15. - (BOOL)isConcurrent { 
  16. return YES; 
  17.  
  18. - (BOOL)isExecuting { 
  19. return executing; 
  20.  
  21. - (BOOL)isFinished { 
  22. return finished; 
  23.  
  24. - (void)start { 
  25. // Always check for cancellation before launching the task. 
  26. if ([self isCancelled]) 
  27. // Must move the operation to the finished state if it is canceled. 
  28. [self willChangeValueForKey:@"isFinished"]; 
  29. finished = YES; 
  30. [self didChangeValueForKey:@"isFinished"]; 
  31. return
  32.  
  33. // If the operation is not canceled, begin executing the task. 
  34. [self willChangeValueForKey:@"isExecuting"]; 
  35. [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil]; 
  36. executing = YES; 
  37. [self didChangeValueForKey:@"isExecuting"]; 
  38.  
  39. - (void)main { 
  40. @try { 
  41. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
  42.  
  43. // Do the main work of the operation here. 
  44.  
  45. [self completeOperation]; 
  46. [pool release]; 
  47. @catch(...) { 
  48. // Do not rethrow exceptions. 
  49.  
  50. - (void)completeOperation { 
  51. [self willChangeValueForKey:@"isFinished"]; 
  52. [self willChangeValueForKey:@"isExecuting"]; 
  53.  
  54. executing = NO; 
  55. finished = YES; 
  56.  
  57. [self didChangeValueForKey:@"isExecuting"]; 
  58. [self didChangeValueForKey:@"isFinished"]; 
  59. @end 

即便操做被取消,你也应该通知KVO observers,你的操做已经完成。当某个operation对象依赖于另外一个operation对象的完成时,它会监测后者的isFinished key path。只有全部依赖的对象都报告已经完成,第一个operation对象才会开始运行。若是你的operation对象没有产生完成通知,就会阻止其它依赖于你的operation对象运行。

维护KVO依从

NSOperation类的key-value observing(KVO)依从于如下key paths:

isCancelled

isConcurrent

isExecuting

isFinished

isReady

dependencies

queuePriority

completionBlock

若是你覆盖start方法,或者对NSOperation对象的其它自定义运行(覆盖main除外),你必须确保自定义对象对这些key paths保留KVO依从。覆盖start方法时,须要关注isExecuting和isFinished两个key paths。

若是你但愿实现依赖于其它东西(非operation对象),你能够覆盖isReady方法,并强制返回NO,直到你等待的依赖获得知足。若是你须要保留默认的依赖管理系统,确保你调用了[super isReady]。当你的operation对象的准备就绪状态发生改变时,生成一个isReady的key path的KVO通知。

除非你覆盖了 addDependency: 或 removeDependency: 方法,不然你不须要关注dependencies key path

虽然你也能够生成 NSOperation 的其它KVO通知,但一般你不须要这样作。若是须要取消一个操做,你能够直接调用现有的cancel方法。相似地,你也不多须要修改queue优先级信息。最后,除非你的operation对象能够动态地改变并发状态,你也不须要提供isConcurrent key path的KVO通知。

自定义一个Operation对象的执行行为

对Operation对象的配置发生在建立对象以后,将其添加到queue以前。

配置operation之间的依赖关系

依赖关系能够顺序地执行相关的operation对象,依赖于其它操做,则必须等到该操做完成以后本身才能开始。你能够建立一对一的依赖关系,也能够建立多个对象之间的依赖图。

使用 NSOperation 的 addDependency: 方法在两个operation对象之间创建依赖关系。表示当前operation对象将依赖于参数指定的目标operation对象。依赖关系不局限于相同queue中的operations对象,Operation对象会管理本身的依赖,所以彻底能够在不一样的queue之间的Operation对象建立依赖关系。

惟一的限制是不能建立环形依赖,这是程序员的错误,全部受影响的operations都没法运行!

当一个operation对象依赖的全部其它对象都已经执行完成,该operation就变成准备执行状态(若是你自定义了isReady方法,则由你的方法肯定是否准备好运行)。若是operation已经在一个queue中,queue就能够在任什么时候候执行这个operation。若是你须要手动执行该operation,就本身调用operation的start方法。

配置依赖必须在运行operation和添加operation到queue以前进行,以后添加的依赖关系可能不起做用。

依赖要求每一个operation对象在状态发生变化时必须发出适当的KVO通知。若是你自定义了operation对象的行为,就必须在自定义代码中生成适当的KVO通知,以确保依赖可以正确地执行。

修改Operation的执行优先级

对于添加到queue的Operations,执行顺序首先由已入队列的operations是否准备好,而后再根据全部operations的相对优先级肯定。是否准备好由对象的依赖关系肯定,优先级等级则是operation对象自己的一个属性。默认全部operation都拥有“普通”优先级,不过你能够经过 setQueuePriority: 方法来提高或下降operation对象的优先级。

优先级只能应用于相同queue中的operations。若是应用有多个operation queue,每一个queue的优先级等级是互相独立的。所以不一样queue中的低优先级操做仍然可能比高优先级操做更早执行。

优先级不能替代依赖关系,优先级只是queue对已经准备好的operations肯定执行顺序。先知足依赖关系,而后再根据优先级从全部准备好的操做中选择优先级最高的那个执行。

修改底层线程的优先级

Mac OS X 10.6以后,咱们能够配置operation底层线程的执行优先级,线程直接由内核管理,一般优先级高的线程会给予更多的执行机会。对于operation对象,你指定线程优先级为0.0到1.0之间的某个数值,0.0表示最低优先级,1.0表示最高优先级。默认线程优先级为0.5

要设置operation的线程优先级,你必须在将operation添加到queue以前,调用 setThreadPriority: 方法进行设置。当queue执行该operation时,默认的start方法会使用你指定的值来修改当前线程的优先级。不过新的线程优先级只在operation的main方法范围内有效。其它全部代码仍然(包括completion block)运行在默认线程优先级。

若是你建立了并发operation,并覆盖了start方法,你必须本身配置线程优先级。

设置一个completion block

在Mac OS X 10.6以后,operation能够在主任务完成以后执行一个completion block。你可使用这个completion block来执行任何不属于主任务的工做。例如你可使用这个block来通知相关的client,操做已经执行完成。而并发operation对象则可使用这个block来产生最终的KVO通知。

调用 NSOperation 的 setCompletionBlock: 方法来设置一个completion block,你传递的block应该没有参数和返回值。

实现Operation对象的技巧

Operation对象的内存管理

operation对象须要良好的内存管理策略

建立你本身的Autorelease Pool

operation是Objective-C对象,你在实现任务的代码中应该建立一个autorelease pool,这样能够保护那些autorelease对象获得尽快地释放。虽然你的自定义代码执行时可能已经有了一个pool,但你不能依赖于这个行为,老是应该本身建立一个。

拥有本身的autorelease pool还能更加灵活地管理operation的内存。若是operation建立大量的临时对象,则能够考虑建立额外的pool,来清理再也不使用的临时对象。在iOS*****别须要注意,应早晚地清理再也不使用的临时对象,避免内存警告。

 
  1. - (void)main { 
  2. @try { 
  3. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
  4.  
  5. // Do the main work of the operation here. 
  6.  
  7. [pool release]; 
  8. @catch(...) { 
  9. // Do not rethrow exceptions. 

避免Per-Thread存储

虽然多数operation都在线程中执行,但对于非并发operation,一般由operation queue提供线程,这时候queue拥有该线程,而你的应用不该该去动这个线程。特别是不要关联任何数据到不是你建立和拥有的线程。这些线程由queue管理,根据系统和应用的需求建立或销毁。所以使用Per-Thread storage在operations之间传递数据是不可靠的,并且颇有可能会失败。

对于operation对象,你彻底没有理由使用Per-Thread Storage,应该在建立对象的时候就给它须要的全部数据。全部输入和输出数据都应该存储在operation对象中,最后再整合到你的应用,或者最终释放掉。

根据须要保留Operation对象的引用

因为operation对象异步执行,你不能建立完之后就彻底无论。它们也是对象,须要你来分配和释放它们管理的任何资源,特别是若是你须要在operation对象完成后获取其中的数据。

因为queue老是尽最大可能快速地调度和执行operation,在你添加operation到queue时,可能当即就开始运行,当你稍后向queue请求operation对象的状态时,有可能queue已经执行完了相应的operation并从queue中删除了这个对象。所以你老是应该本身拥有operation对象的引用。

处理错误和异常

operation本质上是应用中独立的实体,所以须要本身负责处理全部的错误和异常。NSOperation默认的start方法并无捕获异常。因此你本身的代码老是应该捕获并抑制异常。你还应该检查错误代码并适当地通知应用。若是你覆盖了start方法,你也必须捕获全部异常,阻止它离开底层线程的范围。

你须要准备好处理如下错误或异常:

检查并处理UNIX errno风格的错误代码

检查方法或函数显式返回的错误代码

捕获你的代码或系统frameworks抛出的异常

捕获NSOperation类本身抛出的异常,在如下状况NSOperation会抛出异常:

operation没有准备好,可是调用了start方法

operation正在执行或已经完成(可能被取消),再次调用了start方法。

当你添加completion block到正在执行或已经完成的operation

当你试图获取已经取消 NSInvocationOperation 对象的结果

为Operation对象肯定一个适当的范围

和任何对象同样,NSOperation对象也会消耗内存,执行时也会带来开销。所以若是operation对象只作不多的工做,可是却建立成千上万个小的operation对象,你就会发现更多的时间花在了调度operations而不是执行它们。

要高效地使用Operations,关键是在Operation执行的工做量和保持计算机繁忙之间,找到最佳的平衡。确保每一个Operation都有必定的工做量能够执行。例如100个operations执行100次相同任务,能够考虑换成10个operations,每一个执行10次。

你一样要避免向一个queue中添加过多的operations,或者持续快速地向queue中添加operation,超过queue所能处理的能力。这里能够考虑分批建立operations对象,在一批对象执行完以后,使用completion block告诉应用建立下一批operations对象。

执行Operations

应用须要执行Operations来处理相关的工做,你有几种方法来执行Operations对象。

添加Operations到Operation Queue

执行Operations最简单的方法是添加到operation queue,后者是 NSOperationQueue 对象。应用负责建立和维护本身使用的全部 NSOperationQueue 对象。

 
  1. NSOperationQueue* aQueue = [[NSOperationQueue alloc] init]; 

调用 addOperation: 方法添加一个operation到queue,Mac OS X 10.6以后可使用 addOperations:waitUntilFinished: 方法一次添加一组operations,或者也能够直接使用 addOperationWithBlock: 方法添加 block 对象到queue。

 
  1. [aQueue addOperation:anOp]; // Add a single operation 
  2. [aQueue addOperations:anArrayOfOps waitUntilFinished:NO]; // Add multiple operations 
  3. [aQueue addOperationWithBlock:^{ 
  4. /* Do something. */ 
  5. }]; 

Operations添加到queue后,一般短期内就会获得运行。可是若是存在依赖,或者Operations挂起等缘由,也可能须要等待。

注意Operations添加到queue以后,绝对不要再修改Operations对象。由于Operations对象可能会在任什么时候候运行,所以改变依赖或数据会产生不利的影响。你只能经过 NSOperation 的方法来查看操做的状态,是否正在运行、等待运行、已经完成等。

虽然 NSOperationQueue 类设计用于并发执行Operations,你也能够强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount: 方法能够配置operation queue的最大并发操做数量。设为1就表示queue每次只能执行一个操做。不过operation执行的顺序仍然依赖于其它因素,像操做是否准备好和优先级等。所以串行化的operation queue并不等同于Grand Central Dispatch中的串行dispatch queue。

手动执行Operations

手动执行Operation,要求Operation已经准备好,isReady返回YES,此时你才能调用start方法来执行它。isReady方法与Operations依赖是结合在一块儿的。

调用start而不是main来手动执行Operation,由于start在执行你的自定义代码以前,会首先执行一些安全检查。并且start还会产生KVO通知,以正确地支持Operations的依赖机制。start还能处理Operations已经被取消的状况,此时会抛出一个异常。

手动执行Operation对象以前,还须要调用 isConcurrent 方法,若是返回NO,你的代码能够决定在当前线程同步执行这个Operation,或者建立一个独立的线程以异步执行。

下面方法演示了手动执行Operation,若是这个方法返回NO,表示不能执行,你须要设置一个定时器,稍后再次调用本方法,直到这个方法返回YES,表示已经执行Operation。

 
  1. - (BOOL)performOperation:(NSOperation*)anOp 
  2. BOOL        ranIt = NO; 
  3.  
  4. if ([anOp isReady] && ![anOp isCancelled]) 
  5. if (![anOp isConcurrent]) 
  6. [anOp start]; 
  7. else 
  8. [NSThread detachNewThreadSelector:@selector(start) 
  9. toTarget:anOp withObject:nil]; 
  10. ranIt = YES; 
  11. else if ([anOp isCancelled]) 
  12. // If it was canceled before it was started, 
  13. //  move the operation to the finished state. 
  14. [self willChangeValueForKey:@"isFinished"]; 
  15. [self willChangeValueForKey:@"isExecuting"]; 
  16. executing = NO; 
  17. finished = YES; 
  18. [self didChangeValueForKey:@"isExecuting"]; 
  19. [self didChangeValueForKey:@"isFinished"]; 
  20.  
  21. // Set ranIt to YES to prevent the operation from 
  22. // being passed to this method again in the future. 
  23. ranIt = YES; 
  24. return ranIt; 

取消Operations

一旦添加到operation queue,queue就拥有了这个对象而且不能被删除,惟一能作的事情是取消。你能够调用Operation对象的cancel方法取消单个操做,也能够调用operation queue的 cancelAllOperations 方法取消当前queue中的全部操做。

只有你肯定再也不须要Operations对象时,才应该取消它。发出取消命令会将Operations对象设置为"Canceled"状态,会阻止它被执行。因为取消也被认为是完成,依赖于它的其它Operations对象会收到适当的KVO通知,并清除依赖状态,而后获得执行。

所以常见的作法是当发生重大事件时,一次性取消queue中的全部操做,例如应用退出或用户请求取消操做。

等待Operations完成

为了最佳的性能,你应该尽可能设计你的应用尽量地异步操做,让应用在操做正在执行时能够去处理其它事情。

若是建立operation的代码须要处理operation完成后的结果,可使用 NSOperation 的 waitUntilFinished 方法等待operation完成。一般咱们应该避免编写这样的代码,阻塞当前线程多是一种简便的解决方案,可是它引入了更多的串行代码,限制了整个应用的并发性,同时也下降了用户体验。

绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻止主线程将致使应用没法响应用户事件,应用也将表现为无响应。

除了等待单个Operation完成,你也能够同时等待一个queue中的全部操做,使用 NSOperationQueue 的 waitUntilAllOperationsAreFinished 方法。注意在等待一个queue时,应用的其它线程仍然能够往queue中添加Operation,所以可能加长你线程的等待时间。

挂起和继续Queue

若是你想临时挂起Operations的执行,可使用 setSuspended: 方法暂停相应的queue。不过挂起一个queue不会致使正在执行的Operation在任务中途暂停,只是简单地阻止调度新Operation执行。你能够在响应用户请求时,挂起一个queue,来暂停等待中的任务。稍后根据用户的请求,能够再次调用 setSuspended: 方法继续Queue中操做的执行。

1 2  3  4 
相关文章
相关标签/搜索