NSOperation (概述)

来源:互联网 发布:淘宝店铺转化率低 编辑:程序博客网 时间:2024/06/06 07:23

NSOperation类是一个抽象类,你可以用来封装一个与任务相关的代码和数据。因为它是抽象的,所以你不能直接使用这个类,但是你可以使用它的子类或者系统定义的子类(NSInvocationOperation 或NSBlockOperation)来执行具体的任务。尽管是抽象的,但是NSOperaion的基本实现包含了重要的逻辑来协调任务的安全执行。这种存在的内置逻辑允许你去关注你的任务的实际实现,而不是使用中间代码来确保它在其他系统对象上的正确工作。
一个operation对象就是一个单点触发的对象--也就是说,执行它的任务一次,就不能再次执行。你通常将操作添加进操作队列(一个NSOperationQueue实例)来执行这些操作。一个操作队列执行这些操作,要么直接地在子线程中执行,要么间接地使用libdispatch库(也称为Grand Central Dispatch)来执行。要想得到更多关于队列如何执行操作的信息,可以查看 NSOperationQueue Classes Reference.
如果你不想使用操作队列,你可以在你的代码中直接调用它的start方法来执行一个操作。手动地执行操作的确会给你的代码增加负担,因为开始一个不在就绪状态的操作会触发异常。这个ready属性用来表明操作的就绪性。

  • Operation依赖
    要想按照特定的方式来执行操作,依赖是一个的简便方式。通过使用 addDependency: 和removeDependency:方法,你可以增加或者删除对一个操作的依赖。默认情况下,一个有依赖的operation对象直到它的所有依赖opertaion对象执行完才会处于执行的就绪状态。然而,一旦最后一个依赖操作执行完,这个operation对象就会处于就绪状态并且能够执行。
    对于NSOperation所支持的依赖,无论这个依赖操作是否执行成功都不做区分。(也就是说,取消一个操作可以看作是执行完)在这种它的依赖操作被取消或者没有成功地执行它的任务的情况下,这个有依赖的操作是否需要处理是由你来决定的。这也许需要你加入一些额外的错误追踪能力到你的operation对象中。
  • 符合KVO特性的属性
    NSOperation类的一些属性是兼容KVC和KVO特性的。根据需要,你可以监听这些属性来控制你应用中的其它部分。监听这些属性,请使用下面的key paths:
    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
    尽管你可以给这些属性绑定监听者,但是你不应该使用Cocoa的绑定方式,将它们绑定到你的应用界面的元素上。和用户界面相关联的代码通常一定是在应用的主线程上执行的。因为一个operation操作可能在任意线程上执行,和KVO通知关联的operation操作也可能出现在任意线程上。
    如果你对上述的属性提供自定义的实现,你的实现必须保持KVC和KVO的特性。如果你对NSOperation对象定义了其它属性,推荐你将这些属性也遵从KVC和KVO的特性。如何支持KVC的信息,请参考Key-Value Coding Programming Guide.如何支持KVO的信息,请参考Key-Value Observing Programming Guide.

  • 多核情况
    NSOperation类是有它的多核意识的。在多线程中没有创建额外的锁来同步访问对象,调用NSOperation对象的方法也是安全的。这种做法也是必要的,因为一个操作通常在创建和监视它的那个单一线程中执行。
    当你创建NSOperation子类,你必须确保任何被覆写的方法在多线程调用中依然是安全的。如果你在你的子类中实现了自定义的方法,例如自定义数据访问者,你也必须确保这些方法是线程安全的。因此,访问在operation中访问任何数据变量都必须同步访问,以防止潜在的脏数据发生。更多关于同步的信息,请参考Threading Programming Guide.

  • 异步和同步Operations
    如果你打算手动执行一个operation对象,而不是将它加入队列,那么你可以将你的operation以同步或者异步的方式执行。Operation对象默认是同步的。在同步的operation中,这个operation不会创建一个单独的线程去执行它的任务。当你在你的代码中直接调用同步operation的start方法时,这个operation在当前线程中立即执行。当这个对象的start方法返回到调用者的地方时,这个任务执行完毕。
    当你在异步的operation中调用start方法时,这个方法将会在相应的任务执行完之前返回。一个异步的operation对象负责在一个单独的线程中调度它的任务。这个operation操作能够直接开启一个新线程,调用一个异步方法,或者提交一个block到分发队列中来执行。当控制返回给调用者,这个operation依然在运行也是无关紧要的,它仅仅是在运行。
    如果你总是打算使用队列来执行你的operaions,那么它将是像定义同步一样简单。但是,如果你手动执行operaions,你可能想像定义的异步那样来定义你的operation对象。定义一个异步operation需要做很多工作,因为你需要监视正在进行的任务的状态,使用KVO通知报告状态的改变。但是在这种情况下定义异步operation是有用的,例如,你想确保手动执行操作不会阻塞调用线程。
    当你将一个operation添加到operation队列时,这个队列会忽略异步属性的值,并且总是在一个单独的线程中调用start方法。因此,如果你执行操作总是将它们加入到一个队列中,就没有必要将他们做成异步的。
    怎样定义同步和异步的operations,请参考Subclassing Notes.

  • Subclassing Notes
    NSOperation类提供了基本的逻辑来纪录你的操作的执行状态,然而要想做任何实际的工作就必须使用子类。怎么创建你的子类取决于你的operation是需要同步还是异步执行的。
    覆写方法
    对于非并发的操作,你通常仅需要覆写这一个方法:main
    在这个方法中,你把需要执行的代码放在所给的任务上。当然,你需要自定义一个让实例化你的子类更加容易的方法。你可能也想定义在你的operation中访问数据的getter和setter方法。然而,如果你自定义了getter和setter方法的话,你必须确保这些方法可以在多线程中安全访问。
    如果你创建了一个并发operation,你至少需要覆写下面的这些方法和属性:
    start
    asynchronous
    executing
    finished
    在一个并发的operation中,你的start方法负责以一个异步的方法开启操作。无论你是开启一个线程还是调用异步函数,你都需要在这个方法中做。一旦开始这个operation,你的start方法应该根据executing属性来更新operation的执行状态。你可以通过发出关于executing的KVO通知来做到这一点,这样也能让需要的地方知道这个operation正在运行。你的executing属性必须以一种线程安全的方式提供状态。
    一旦它的任务被完成或者取消,你的并发operation对象必须发出关于isExecuting和isFinished两个属性的KVO通知,来标记operation的最终变化状态。(就取消的情况而言,即使这个operation没有完全地完成它的任务,更新isFinished的值也是重要的。因为被加入到队列的operation必须根据它们被完成才能从队列中移除)除了生成KVO的通知,你覆写的executing和finished属性也需要根据operation的状态继续跟踪准确的值。
    更多关于如何定义并发operation的信息,请参考Concurrency Programming Guide。
    重要(提示)
    在你的start方法中,任何时候你都不应该调用super方法。当你定义一个并发operation时,你自己要提供默认的start方法所提供的相同的表现行为,这些行为包括开启任务和发出正确的KVO通知。在start方法中你应该检查在实际开启一个任务前,这个operation是否被取消了。更多关于取消的语义,请参考Responding to the Cancel Command。
    对于并发队列,除了上面描述的方法基本上没有需要被覆写的方法了。然而,如果你定制了operation的依赖特性,你可能必须覆写额外的方法同时提供额外的KVO通知。就依赖而言,这可能仅需要提供isReady的通知。因为dependencies属性包含了一系列的依赖操作,改变这个属性值的操作已经由默认的NSOperation类处理了。

  • 维持Operation对象状态
    Operation对象维护内部的状态信息来决定什么时候执行是安全的以及通过operation的生命周期通知外部的使用者的进展。在你的代码中,你定制的子类必需维护状态信息来确保operation的正确执行。表1列出了和operation相关的状态属性,以及在你的子类中如何管理这些属性。

键值 描述 isReady isReady属性值让使用者知道operation什么时候处于执行的就绪状态。ready属性值包含YES和NO.当为YES时,这个operation是处于执行的就绪状态的,当为NO时,表示它依赖的operation未完成。大多数情况下,你不用自己管理这个状态。如果处于就绪状态的operation是由除了它依赖的operation之外的因素决定,例如你程序中的外在条件,那么你可以提供这个ready属性自己的实现以及自己纪录这个operation的就绪状态。当外在的状态允许的情况下,创建一个operation对象通常是简单的。在OSX v10.6及以后版本,当一个operation正在等待一个或多个依赖操作完成的时候,你可以取消这个operation。这些依赖此后被忽略而且这个属性的值被更新到就绪状态。这种做法让操作队列移除取消的operation更迅速。 isExecuting isExecuting属性值让使用者知道这个operation是否正在执行分配给它的任务。如果这个operation正在执行它的任务,那么这个属性值必需是YES,否则为NO.如果你的operation对象的start方法重写了,当你的operation改变了执行状态时你必须重写这个executing属性以及生成KVO通知。 isFinished isFinished属性值让调用者知道operation成功完成任务或者被取消。一个operation对象直到isFinished属性值变为YES时,才会清除依赖。同样地,操作队列直到finished属性值为YES时才回将一个operation移除队列。因此防止正在进行或者取消的operation阻塞队列,将operation标记为完成是重要的。如果你的operation对象的start方法重写了,当operation执行完成或者被取消的时候,你必须重写这个finished属性以及生成KVO通知。 isCancelled isCancelled属性值让调用者知道operation是否被取消。支持取消特性是自愿的但被提倡,而且你的代码中不用发送KVO通知。在一个operation中取消操作的注意点在Responding to the Cancel Command中有更多的描述信息。
  • 对取消命令的响应(Responding to the Cancel Command)
    一旦你将一个operation添加到队列,这个operation就不再由你处理了。队列接管和处理了任务的调度。然而,如果你后来决定不想执行operation了--因为用户在进度条上点击了一个取消按钮或者停止了应用--你可以取消operation来阻止无必要的CPU时间消耗。你可以通过调用operation的cancel方法来做,也可以通过调用NSOperationQueue这个类的cancelAllOperations方法来做。
    取消operation不会立即执行使它停止。尽管重视cancelled属性的值是所有operation的期望,但是你的代码必需显示地检查这个属性值以及根据需要废弃。NSOperation默认的实现包含了检查取消操作。例如,如果你在调用start方法之前取消了一个operation,这个start方法没有开始任务就回停止。
    注意:
    在OS X v10.6中,cancel方法的行为取决于这个operation是否在操作队列中。对于未加入到队列的operations,这个方法标记operation为立即结束,生成合适的KVO通知。对于加入到队列的operations,简单地标记operation为就绪状态,让队列调用它的start方法,随后执行完从队列中清除这个operation。
    你应该在你写的自定义代码中总是支持cancellation语义。特别地,你的主任务代码应该周期性的检查cancelled这个属性的值。如果这个属性值为YES,你的operation对象应该尽可能快的清理和退出。如果你自定义了start方法的实现,这个方法应该包含提前检查取消的行为。你自己的start方法必需准备处理这种提前取消的情况。
    当一个operation被取消的时候,除了简单的退出,你将一个取消的operation置为合适的最终状态也是重要的。特别地,如果你自己管理finished和executing属性值(也许你正在实现一个并发operation),你必须保持这些属性值的一致性。特别地,你必须将finished置为YES,executing置为NO。即使这个operation在它开始执行前被取消了,你也必须做出更新。
0 0
原创粉丝点击