NSOperation的简单介绍

2018-06-06  本文已影响75人  哆啦_

NSOperation是一个抽象类,代表与单个任务相关联的代码和数据。

由于NSOperation是一个抽象类,所以不直接使用它而是使用它的子类或者系统定义的子类(NSInvocationOperation或者NSBlockOperation)来执行任务,尽管是一个抽象类,NSOperation的基本实现包含了重要的逻辑来协调任务安全的执行。这种内置的逻辑使得我们可以专注于任务的具体实现。

operation对象是一次性对象,也就是说它只执行一次任务,不能重复使用。通常把operation添加到一个 操作队列(NSOperationQueue 类的实例)来执行。 操作队列 可以在子线程中直接执行操作,或者使用GCD间接的执行。

如果不想使用操作队列,我们可以直接在代码中调用operation的start方法来执行操作。 手动执行操作确实给代码带来更多的负担。因为启动一个未ready状态的操作会引发异常。ready属性指示操作的准备情况

操作依赖性

依赖性是以特定顺序执行操作的便捷方式。 可以使用- (void)addDependency:(NSOperation *)op;- (void)removeDependency:(NSOperation *)op; 方法来为操作添加或移除依赖。默认情况下,operation对象直到它依赖的所有的operation对象都执行完毕之后才会被视为准备就绪。一旦最后依赖的操作完成,该operation对象就准备就绪并能够执行

NSOperation的依赖关系不区分关联操作是否成功finished。(换句话说,取消某个操作也会被标记为finished)。在其依赖操作被取消或未成功完成任务的情况下,是否继续执行具有依赖关系的操作取决于我们自己。这可能需要将一些额外的错误跟踪功能添加到operation对象

KVO 兼容属性

NSOperation类的几个属性是支持KVC和KVO的,根据需要,我们可以监测这些属性来控制应用程序的其他部分。使用一下路径监测属性:

  • isCancelled - read-only

  • isAsynchronous - read-only

  • isExecuting - read-only

  • isFinished - read-only

  • isReady - read-only

  • dependencies - read-only

  • queuePriority - readable and writable

  • completionBlock - readable and writable

虽然我们可以观察这些属性,但是不应该将这些属性跟UI元素绑定,因为通常情况下UI元素的操作只能在主线程,而KVO通知可能在任何线程中。

如果为任何上述属性提供自定义实现,则自定义实现必须支持KVC和KVO。 如果自定义的NSOperation对象定义了其他属性,建议也使这些属性支持KVC和KVO。

多核考虑

NSOperation类本身是多核的。因此多线程访问是安全的,不需要创建额外的锁来同步访问。之所以这么设计是因为operation通常在创建并正在监视它的单独线程中运行。

当子类化NSOperation类时,必须确保重写的方法是线程安全的。如果在子类中实现了自定义的方法,必须确定这些方法是线程安全的。因此,必须同步访问operation的任何数据变量,以防止潜在的数据损坏。

异步和同步操作

如果要手动执行operation,而不是将其添加到一个队列中。则可以将operation设计为是同步或异步执行。 operation对象默认是同步的,在同步操作中,operation对象不会创建一个单独的线程来执行任务。当你调用同步操作的start方法时,该操作会在当前线程立即执行。当对象的start方法将控制权交还给调用者时,它本身的任务就完成了

当调用异步操作的start方法时,该方法可能会在其相应的任务完成之前返回。异步操作对象负责在单独的线程上调度它的任务。该操作可以通过直接开启新的线程,调用异步方法,或将block提交到调度队列执行来实现。当控制权返回给调用者时,操作是否正在进行并不重要,只是它可能正在运行。

如果使用队列来执行operation,使用同步操作更简单。如果手动执行操作,则可能需要将操作定义为异步。 定义一个异步操作需要更多工作,因为必须监测任务的持续状态,并且使用KVO通知监测该状态下的更改。但是,如果想要确保手动执行的操作不会阻塞线程,异步操作将会很有用。

当添加一个操作到操作队列中,队列会忽略操作的asynchronous属性的值, 并始终从一个单独的线程调用start方法。 因此,如果将operation添加到操作队列中来执行的话,就没有理由使这些operation异步。

子类化说明

NSOperation类提供了追踪操作执行状态的基本逻辑,但是除此之外,必须子类化来完成实际工作。如何创建子类取决于操作是并发执行还是非并发执行。

重写的方法

对于非并发操作,通常只需要重写一个方法:

- (void)main;

在这个方法中,放置执行给定任务所需的代码。当然,也可以自定义初始化方法,来方便的创建实例。我们可能还需要定义一些getter和setter方法来访问operation中的数据,但是,如果定义了自定义的getter/setter方法,必须保证这些方法是线程安全的

如果创建并发操作,则需要至少覆盖以下方法和属性

在并发操作中,start方法负责以异步方式启动操作。无论是生成一个线程还是调用一个异步方法,都可以通过该方法来完成。开始操作时,start方法应该更新executing属性(表示操作的执行状态)。可以通过发送KVO通知,让外部知道操作正在运行。executing属性必须是线程安全的。

完成或取消任务后,并发操作对象必须为isExecutingisFinished路径生成KVO通知,标记操作状态的最终更改。(在取消的情况下,即使操作没有结束它的任务,也要更新isFinished。 排队的操作在从队列中删除之前必须是已经finished)。除了生成KVO通知,重写的executingfinished属性也应该根据操作状态作出相应的更改。

重点:

在自定义的start方法中,任何时候都不应该调用super。当定义一个并发操作,需要自己提供跟默认start方法相同的行为,其中包括启动任务并生成合适的KVO通知。自定义的start方法也应该检查在实际启动任务之前操作本身是否已经被取消。

即使是并发队列,也很少需要重写上述描述以外的方法。 然而,如果你自定义operation 的依赖功能,则还必须重写其他的方法和提供KVO通知。在有依赖关系的情况下,可能只需要提供isReady路径的通知。 因为dependencies属性包含了依赖操作的列表,对它的更改已由默认的NSOperation类处理

维护operation对象状态

operation对象在内部维护状态信息以确定什么时候执行操作是安全的,并通过operation的生命周期通知外部客户的进展情况。自定义的子类需要维护该状态信息以确保代码中的操作正确的执行。 相关的operation状态的key path:

响应取消命令

一旦添加operation到队列中,operation就脱离你的控制了。队列会接管并处理任务的调度。但是,如果稍后决定不想执行operation,可能是因为用户点击取消按钮或退出了应用程序,比如,你可以取消operation来防止它不必要的占用CPU时间。可以通过operation对象的cancel方法或- (void)cancelAllOperations;方法。

取消一个operation不会立即强制停止正在执行的操作。代码中必须显式的检查该属性的值并根据需要中止。NSOperation的默认实现包括检查取消状态。比如,如果在调用start方法之前取消了一个操作,start方法将退出而不启动该任务。

在任何自定义代码中,都应该始终支持取消语义。特别的,主要任务的代码应该定期性的检查cancelled属性的值。如果为YES,operation对象应该尽可能快的清理并退出。如果实现了自定义的start方法,那么该方法应该适当的提前检查取消状态。

除了当操作取消时退出操作,将取消的操作设置为一个合适的最终状态也是很重要的。如果我们自己管理finishedexecuting属性的值(可能是因为正在执行并发操作), 必须相应地更新这些属性。特别的,必须将finished属性的值返回为YES,executing属性的值为NO。即使操作在开始执行之前就被取消也必须进行这些更改。

几个方法的简单介绍

执行操作的方法
取消操作的方法
属性
管理依赖

NSBlockOperation

NSBlockOperation类是NSOperation的一个具体子类,用于管理一个或多个block的并发执行。可以使用该对象一次执行数个block,而不必为每个block创建单独的操作对象。当执行多个block时,只有当所有的block执行结束之后,该操作对象才会被视为finished。

添加到NSBlockOperation的block以默认优先级调度到适当的工作队列。block本身不应该对其执行环境做任何假设

方法介绍

NSInvocationOperation

NSInvocationOperation类是NSOperation的一个具体子类,用于开启一个操作,该操作包括在指定对象上调用一个selector。

方法

NSOperation与GCD

尽管GCD对于内嵌异步操作十分理想,NSOperation依旧提供更复杂、面向对象的计算模型,它对于涉及到各种类型数据、需要重复处理的任务是更加理想的

什么时候使用GCD

Dispatch queues, groups, semaphores(信号量), sources, 和 barriers 组成了一组基本的并发基元,其上构建了所有的系统框架。

对于一次性的计算或简单的加速现有方法,使用轻量级的GCD比使用NSOperation更方便。

什么时候使用NSOperation

NSOperation 可以按照特定的队列优先级和服务质量(qualityOfService)来调度一组依赖关系。与在GCD队列中的block不同, NSOperation可以被取消,以及查询其操作状态。通过继承,NSOperation可以将其工作结果与自身关联起来,供将来参考。


资料来源:
上一篇 下一篇

猜你喜欢

热点阅读