NSOperation的进阶使用和简单探讨

本文将会从多个方面探讨NSOperation类和NSOperationQueue类的相关内容

一、简介

NSOperation类是iOS2.0推出的,通过NSThread实现的,但是效率一般。 从OS X10.6和iOS4推出GCD时,又重写了NSOperation和NSOperationQueue,NSOperation和NSOperationQueue分别对应GCD的任务和队列,所以NSOPeration和NSOperationQueue是基于GCD更高一层的封装,而且完全地面向对象。但是比GCD更简单易用、代码可读性也更高。NSOperation和NSOperationQueue对比GCD会带来一点额外的系统开销,但是可以在多个操作Operation中添加附属。

二、知识概括

从NSOperation的思维导图了解的这个类相关的整体的知识点:

NSOperation和NSOperationQueue是基于GCD的更高一层的封装,分别对应GCD的任务和队列,完全地面向对象。可以通过start方法直接启动NSOperation子类对象,并且默认同步执行任务,将NSOperation子类对象添加到NSOperationQueue中,该队列默认并发的调度任务。

开启操作有二种方式,一是通过start方法直接启动操作,该操作默认同步执行,二是将操作添加到NSOperationQueue中,然后由系统从队列中获取操作然后添加到一个新线程中执行,这些操作默认并发执行。

具体实现如下:

方式一:直接由NSOperation子类对象启动。 首先将需要执行的操作封装到NSOperation子类对象中,然后该对象调用Start方法。

方式二:当添加到NSOperationQueue对象中,由该队列对象启动操作。

1.将需要执行的操作封装到NSOperation子类对象中
2.将该对象添加到NSOperationQueue中
3.系统将NSOperation子类对象从NSOperationQueue中取出
4.将取出的操作放到一个新线程中执行

使用队列来执行操作,分为2个阶段:第一阶段:添加到线程队列的过程,是上图的步骤1和2。第二阶段:系统自动从队列中取出线程,并且自动放到线程中执行,是上图的步骤3和4。

接下来相关内容的总结:

1. NSOperation

NSOperation是一个和任务相关的抽象类,不具备封装操作的能力,必须使用其子类。 使用NSOperation⼦类的方式有3种:

  • 系统实现的具体子类:NSInvocationOperation
  • 系统实现的具体子类:NSBlockOperation
  • 自定义子类,实现内部相应的⽅法 该类是线程安全的,不必管理线程生命周期和同步等问题。

a. NSInvocationOperation子类

NSInvocationOperation是NSOperation的子类。创建操作对象的方式有2种,使用initWithTarget:selector:object:创建sel参数是一个或0个的操作对象。使用initWithInvocation:方法,添加sel参数是0个或多个操作对象。在未添加到队列的情况下,创建操作对象的过程中不会开辟线程,会在当前线程中执行同步操作。创建完成后,直接调用start方法,会启动操作对象来执行操,或者添加到NSOperationQueue队列中。无论使用该子类的哪个在初始化的方法,都会在添加一个任务。 和NSBlockOperation子类不同的是,因为没有额外添加任务的方法,使用NSInvocationOperation创建的对象只会有一个任务。

默认情况下,调用start方法不会开辟一个新线程去执行操作,而是在当前线程同步执行任务。只有将其放到一个NSOperationQueue中,才会异步执行操作

b. NSBlockOperation子类

可以通过blockOperationWithBlock:创建NSBlockOperation对象,在创建的时候也添加一个任务。如果想添加更多的任务,可以使用addExecutionBlock:方法。也可以通过init:创建NSBlockOperation对象。但是这种创建方式并不会在创建对象的时候添加任务,同样可以使用addExecutionBlock:方法添加任务。对于启动操作和NSInvocationOperation类一样,都可以通过调用start方法和添加NSOperationQueue中来执行操作。

关于任务的的同步、异步的执行可以总结几点:

1.任务数为1时,即使用blockOperationWithBlock:方法或者init:addExecutionBlock:二个方法结合的方式创建的唯一一个任务时,不会开辟新线程,直接在当前线程同步执行任务。
2.任务数大于1时,使用blockOperationWithBlock:方法或者init:addExecutionBlock:二个方法结合的方式创建的一个任务A,不会开辟线程,直接在当前线程同步执行任务。而NSBlockOperation对象使用addExecutionBlock:方法添加的其他任务会开辟新线程,异步执行任务。
3.将操作放到一个NSOperationQueue中,会异步执行操作任务。

注意:不可在completionBlock属性的block中追加任务,因为在操作已经启动执行中或者结束后不可以添加block任务。

c. 自定义子类

一般类NSInvocationOperation、NSBlockOperation就可以满足使用需要,当然还可以自己自定义子类。

创建的子类时,需要考虑到可能会添加到串行和并发队列的不同情况,需要重写不同的方法。对于串行操作,仅仅需要重新main方法就行,在这个方法中添加想要实现的功能。对于并发操作,重写四个方法:startasynchronousexecutingfinished。并且需要自己创建自动释放池,因为异步操作无法访问主线程的自动释放池。

注意:在自定义子类时,经常通过cancelled属性检查方法是否取消,并且对取消的做出响应。

2. NSOperationQueue

使用将NSOperation对象添加NSOperationQueue中,来管理操作对象是非常方便的。因为当我们把操作对象添加到NSOperationQueue对象后,该NSOperationQueue对象从线程中拿取操作、以及分配到对应线程的工作都是由系统处理的。

只要是创建了队列,在队列中的操作,就会在子线程中执行,并且默认并发操作。添加到子队列NSOperationQueue实例中的操作,都是异步执行

a.操作对象添加到NSOperationQueue对象中

添加的方式有3种。

  • addOperation:添加一个操作
  • addOperationWithBlock:,系统自动封装成一个NSBlockOperation对象,然后添加到队列中
  • addOperations:waitUntilFinished:添加多个操作 操作对象添加到NSOperationQueue之后,通常短时间内就会运行。但是如果存在依赖,或者整个队列被暂停等原因,也可能需要等待。

操作对象添加NSOperationQueue中后,不要再修改操作对象的状态。因为操作对象可能会在任何时候运行,因此改变操作对象的依赖或数据会产生无法预估的问题。只能查看操作对象的状态, 比如是否正在运行、等待运行、已经完成等。

b. 设置最多并发数

虽然NSOperationQueue类设计用于并发执行操作,但是也可以强制让单个队列一次只能调度一个操作对象。setMaxConcurrentOperationCount:方法可以设置队列的最大并发操作数量。当设为1就表示NSOperationQueue实例每次只能执行一个NSOperation子类对象。不过操作对象执行的顺序会依赖于其它因素,比如操作是否准备好和操作对象的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue。

maxConcurrentOperationCount默认是-1,不可设置为0。如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的。

相关概念:

1.并发数: NSOperationQueue队列里同时能调度的NSOperation对象数。
2.最大并发数: 同一时间最多能调度的NSOperation对象数。

c. 进度修改

一个操作执行还未完成时,我们可能需要让该任务暂停、可能之后在进行某些操作后又希望继续执行。为了满足这个需要,苹果公司,为我们提供了suspended属性。当可能我们不想执行某些操作时,可以个cancel方法、cancelAllOperations方法可以取消操作对象,一旦调用了这2个方法,操作对象将无法恢复。具体如下:

对于暂停操作,当NSOperationQueue对象属性suspended设置为YES,队列会停止对任务调度。对那些还在线程中的操作有影响的。如果任务正在执行将不会受到影响,因为任务已经被队列调度到一个线程上并执行。

对于继续操作,当属性suspended设置为NO会继续执行线程操作。队列将积极启动队列中已准备执行的操作。

一旦NSOperation子类操作对象添加到NSOperationQueue对象中,该队列就拥有了该操作对象并且不能删除操作对象,如果不想执行操作对象,只能取消该操作对象。关于取消操作,可以分为2种情况,取消一个操作和取消一个队列的全部操作二种情况。调用NSOperation类实例的cancel方法取消单个操作对象。调用NSOperationQueue类实例cancelAllOperations方法取消队列中全部操作对象。

对于队列中的操作,只有操作标记为已结束才能被队列移除。在队列中未被调度的操作,会调用start方法执行操作,以便操作对象处理取消事件。然后标记这些操作对象为已结束。对于正在线程中执行其任务的操作对象,正在执行的任务会继续执行,该操作对象会被标记经结束。

注意:只会停止调度队列中操作对象,正在执行任务的依然会执行,且取消不可恢复。

d.作用

NSOperation对象可以调⽤start⽅法来执⾏任务,但默认是同步执行的(可以创建异步操作,NSBlockOperation添加操作数大于1时,除第一个任务外,其任务就是异步执行)。如果将NSOperation添加到NSOperationQueue中,之后操作就就由系统管理,系统先从队列中取出操作,然后放到一个新线程中异步执行。总结:添加操作到NSOperationQueue中,自动执行操作,自动开启线程

f. 获取队列

系统提供了2个,可以获取当前队列和主队列。可以通过类属性currentQueue获取当前队列。可以通过类属性mainQueue获取主队列.

3.依赖

操作对象可以添加和移除依赖。当一个操作对象添加了依赖,被依赖的操作对象就会先执行,当被依赖的操作对象执行完才会当前的操作对象。添加到不同线程对象中的操作对象依然彼此间可以单方面依赖。切记循环依赖的情况。这样会产生死循环。

可以通过addDependency方法添加一个或者多个依赖的对象。eg:[A addDependency:B];

操作A依赖于操作B。操作对象会管理自己的依赖,因此在不相同队列中的操作对象可以建立依赖关系。但是**一定要在添加线程对象NSOperationQueue之前,进行依赖设置。**设置依赖可以保证执行顺序,操作添加到队列添加的顺序并不能决定执行顺序,执行的顺序取决于多种因素比如依赖、优先级等。

调用removeDependency:方法移除依赖。

如图,箭头方向就是依赖的对象,从图中可知,A依赖b,而b依赖C。所以三者的执行顺序是C–>b–>A

4.线程安全

在NSOperation实例在多线程上执行是安全的,不需要添加额外的锁

5.cancel方法

只会对未执行的操作有效,正在执行的操作,在收到cancel消息后,依然会执行。

调用操作队列中的操作的cancel方法,且该操作队列具有未完成的依赖操作时,那么这些依赖操作会被忽略。由于操作已经被取消,因此此行为允许队列调用操作的start方法,以便在不调用其主方法的情况下从队列中删除操作。如果对不在队列中的操作调用cancel方法,则该操作立即标记为已取消。

6.状态属性

一个线程有未创建、就绪、运行中、阻塞、消亡等多个状态。而操作对象也有多种状态:executing(执行中)、finished(完成)、ready(就绪)状态,这三个属性是苹果公司,提供给我们用于观察操作对象状态的时候用的。因为这个三个属性KVC与KVO兼容的,因此可以监听操作对象状态属性。

7.操作完成

a. 监听操作完成 当我们可能需要在某个操作对象完成后添加一些功能,此时就可以用属性completionBlock来添加额外的内容了。

operation.completionBlock = ^{// 完成操作后,可以追加的内容
}; 

b. 等待操作完成

这个有2种情况:一是等待单个操作对象,而是等待队列里全部的操作。

如果想等待整个队列的操作,可以同时等待一个queue中的所有操作。使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。在等待一个队列时,应用的其它线程仍然可以往队列中添加操作,因此可能会加长线程的等待时间。

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

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

// 会阻塞当前线程,等到某个operation执行完毕
[op
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值