一、简介

一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的。

创建一个操作队列:

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


二、添加NSOperation到NSOperationQueue中

1.添加一个operation

  1. [queue addOperation:operation];

2.添加一组operation

  1. [queue addOperations:operations waitUntilFinished:NO];

3.添加一个block形式的operation

  1. [queue addOperationWithBlock:^() {
  2. NSLog(@"执行一个新的操作,线程:%@", [NSThread currentThread]);
  3. }];

NSOperation添加到queue之后,通常短时间内就会得到运行。但是如果存在依赖,或者整个queue被暂停等原因,也可能需要等待。

注意:NSOperation添加到queue之后,绝对不要再修改NSOperation对象的状态。因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或数据会产生不利的影响。你只能查看NSOperation对象的状态, 比如是否正在运行、等待运行、已经完成等

三、添加NSOperation的依赖对象
1.当某个NSOperation对象依赖于其它NSOperation对象的完成时,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。

  1. [operation2 addDependency:operation1];

依赖关系不局限于相同queue中的NSOperation对象,NSOperation对象会管理自己的依赖, 因此完全可以在不同的queue之间的NSOperation对象创建依赖关系

唯一的限制是不能创建环形依赖,比如A依赖B,B依赖A,这是错误的

2.依赖关系会影响到NSOperation对象在queue中的执行顺序,看下面的例子:

1> 没有设置依赖关系

  1. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  2. NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
  3. NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
  4. }];
  5. NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
  6. NSLog(@"执行第2次操作,线程:%@", [NSThread currentThread]);
  7. }];
  8. [queue addOperation:operation1];
  9. [queue addOperation:operation2];

打印信息:

  1. 2013-02-03 00:21:35.024 thread[5616:3d13] 执行第1次操作,线程:<NSThread: 0x7658570>{name = (null), num = 3}
  2. 2013-02-03 00:21:35.063 thread[5616:1303] 执行第2次操作,线程:<NSThread: 0x765a2e0>{name = (null), num = 4}

可以看出,默认是按照添加顺序执行的,先执行operation1,再执行operation2

2> 设置了依赖关系

  1. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  2. NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
  3. NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
  4. }];
  5. NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
  6. NSLog(@"执行第2次操作,线程:%@", [NSThread currentThread]);
  7. }];
  8. // operation1依赖于operation2
  9. [operation1 addDependency:operation2];
  10. [queue addOperation:operation1];
  11. [queue addOperation:operation2];

打印信息:

  1. 2013-02-03 00:24:16.260 thread[5656:1b03] 执行第2次操作,线程:<NSThread: 0x7634490>{name = (null), num = 3}
  2. 2013-02-03 00:24:16.285 thread[5656:1303] 执行第1次操作,线程:<NSThread: 0x9138b50>{name = (null), num = 4}

可以看出,先执行operation2,再执行operation1

四、修改Operations的执行顺序

对于添加到queue中的operations,它们的执行顺序取决于2点:

1.首先看看NSOperation是否已经准备好:是否准备好由对象的依赖关系确定

2.然后再根据所有NSOperation的相对优先级来确定。优先级等级则是operation对象本身的一个属性。默认所有operation都拥有“普通”优先级,不过可以通过setQueuePriority:方法来提升或降低operation对象的优先级。优先级只能应用于相同queue中的operations。如果应用有多个operation queue,每个queue的优先级等级是互相独立的。因此不同queue中的低优先级操作仍然可能比高优先级操作更早执行。

注意:优先级不能替代依赖关系,优先级只是对已经准备好的 operations确定执行顺序。先满足依赖关系,然后再根据优先级从所有准备好的操作中选择优先级最高的那个执行。

五、设置队列的最大并发操作数量

队列的最大并发操作数量,意思是队列中最多同时运行几条线程

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

  1. // 每次只能执行一个操作
  2. queue.maxConcurrentOperationCount = 1;
  3. // 或者这样写
  4. [queue setMaxConcurrentOperationCount:1];

六、取消Operations

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

  1. // 取消单个操作
  2. [operation cancel];
  3. // 取消queue中所有的操作
  4. [queue cancelAllOperations];

七、等待Options完成

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

  1. // 会阻塞当前线程,等到某个operation执行完毕
  2. [operation waitUntilFinished];

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

  1. // 阻塞当前线程,等待queue的所有操作执行完毕
  2. [queue waitUntilAllOperationsAreFinished];

八、暂停和继续queue

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

  1. // 暂停queue
  2. [queue setSuspended:YES];
  3. // 继续queue
  4. [queue setSuspended:NO];

NSOperationQueue的更多相关文章

  1. Cocoa深入学习:NSOperationQueue、NSRunLoop和线程安全 (转)

    目前在 iOS 和 OS X 中有两套先进的同步 API 可供我们使用:NSOperation 和 GCD .其中 GCD 是基于 C 的底层的 API ,而 NSOperation 则是 GCD 实 ...

  2. 多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用

    本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下 ...

  3. NSOperationQueue的其他方法

    1.设置最大并发数 什么是并发数 同时执行的任务数 比如,同时开3个线程执行3个任务,并发数就是3   最大并发数的相关方法 - (NSInteger)maxConcurrentOperationCo ...

  4. NSOperationQueue的基本使用

    NSOperationQueue的作用 NSOperation可以调用start方法来执行任务,但默认是同步执行的 如果将NSOperation添加到NSOperationQueue(操作队列)中,系 ...

  5. NSOperationQueue与GCD的使用原则和场景

    首先,我们要明确NSOperationQueue与GCD之间的关系: NSOpertaionQueue用GCD构建封装的,是GCD的高级抽象. 其次,我们要区别两者的不同: GCD仅仅支持FIFO队列 ...

  6. 伟大的GCD和NSOperationQueue

    一. GCD GCD中最重要的两个东西 任务 和 队列 任务就是一段代码(用来缓存,下载,计算等操作) 队列从大的方面分为两个队列:主队列(串行队列)和 自己创建的队列(串行,和并行) 主队列中: 在 ...

  7. 多线程NSInvocationOperation(NSOperationQueue)的基本用法

        #import "ViewController.h" @interface ViewController () @end @implementation ViewContr ...

  8. IOS 多线程02-pthread 、 NSThread 、GCD 、NSOperationQueue、NSRunLoop

    注:本人是翻译过来,并且加上本人的一点见解. 要点: 1.前言 2.pthread 3.NSThread 4.Grand Central Dispatch(GCD) 5.Operation Queue ...

  9. GCD与NSOperationQueue

    1> GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装 2> GCD只支持FIFO(先入先出)的队列,NSOperationQueue可以很方便地调整执 ...

  10. 多线程编程4 - NSOperationQueue

    一.简介 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的.也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步 ...

随机推荐

  1. 《C#高级编程》学习笔记------C#中的委托和事件(续)

    本文转载自张子阳 目录 为什么要使用事件而不是委托变量? 为什么委托定义的返回值通常都为void? 如何让事件只允许一个客户订阅?(事件访问器) 获得多个返回值与异常处理 委托中订阅者方法超时的处理 ...

  2. 《ASP.NET MVC4 WEB编程》学习笔记------.net mvc实现原理ActionResult/View

    ActionResult ActionResult是Action的返回结果.ActionResult 有多个派生类,每个子类功能均不同,并不是所有的子类都需要返回视图View,有些直接返回流,有些返回 ...

  3. C++类编程(一)const的使用

    设计类时,考虑以下五点 1.构造函数初始化列表 2.函数该不该加const 3.参数传递尽量考虑用引用传递,考虑加不加const 4.返回用不用引用 5.数据尽量放在private,函数尽量放在pub ...

  4. 手动创建第一个JSP应用

    当按照上一篇文章所说的,配置好了tomcat服务器之后,我们就可以开发运行在tomcat上的第一个web应用了,在对tomcat的目录了解之后,我们知道webapps是存放我们编写相应应用程序的目录, ...

  5. Java集合框架中Map接口的使用

    在我们常用的Java集合框架接口中,除了前面说过的Collection接口以及他的根接口List接口和Set接口的使用,Map接口也是一个经常使用的接口,和Collection接口不同,Map接口并不 ...

  6. Python多进程(1)——subprocess与Popen()

    Python多进程方面涉及的模块主要包括: subprocess:可以在当前程序中执行其他程序或命令: mmap:提供一种基于内存的进程间通信机制: multiprocessing:提供支持多处理器技 ...

  7. MFC添加鼠标相应事件

    Class View (类视图)窗口中选中 你要添加事件的类(比如C***View.CPP),切换到properties窗口. 点击上面的Message图标(在Event图标[一个闪电形状的图标] 右 ...

  8. 说说localStorage

    HTML5的本地存储是大势所趋,如果仅存储在内存中,则是sessionStorage,他们的语法都是一样,仅仅是一个存储在本地文件系统中,另一个存储在内存中(随着浏览器的关闭而消失),其语句如下: l ...

  9. 5.建造者模式(Builder Pattern)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. PowerDesigner12逆向生成oracle数据表时,错误解决

    1.用PowerDesigner12建模,在Database—>Generate Database (或者用Ctrl+G快捷键)来生产sql语句,却提示“Generation aborted d ...