要创建一个真正具有响应式的iOS应用程序,你必须保持长时间的运行以便操作UI线程,并注意避免任何延迟事件阻塞UI线程。这意味着你将需要在后台执行各种操作。为方便起见,我们增加了一个类BFTask。一个可以表示异步操作结果的任务(task)。通常情况下,一个BFTask从异步函数返回,并给出一个继续处理任务结果的能力。当一个任务从一个函数返回它已经开始做的工作。一个任务是不依赖于特定的线程模型:它代表正在做的工作,而不是在那里执行。task比异步编程的其它方法,诸如回调有更多优点。 BFTask是不能代替NSOperation或GCD的。事实上,他们发挥得很好。但是BFTask填补了它们中的一些空白,以及这些技术没有解决的一些问题。
BFTask负责管理依赖。与使用NSOperation的依赖管理相比,你没有必要开始BFTask之前声明的所有依赖关系。例如,假设你需要保存一组对象,每个人可能或不可能需要保存子对象。在NSOperation,您通常需要提前创建操作为每个子操作节省时间。但是,你总是不知道你何时开始工作,是否它是必要的。这让管理依赖关系的NSOperation非常痛苦。即使在最好的情况下,必须依赖于它们的操作,导致出现了比它执行不同的命令代码之前创建的依赖。使用BFTask,您可以在操作的过程中决定是否将子任务返回,在一个很短的时间内。
BFTasks释放依赖关系。NSOperation强引用了这些依赖项,所以如果你有队列顺序地执行这些依赖项,你的应用将会产生泄漏,因为每次的操作永远会被retain。BFTasks会释放它们的回调,当这些操作在运行中的时候,所以清理BFTask后,它持有的一切东西都将被清空。这可以减少内存使用,并简化内存管理。
BFTasks跟踪完成任务的状态:它跟踪是否有返回值,任务被取消,或者如果发生错误。它还具有方便的方法来返回错误信息。在NSOperation里,你自己必须建立所有这些东西。
BFTasks不依赖于任何特定的线程模型。所以它可以简单地使用一个操作队列执行几个任务,当其他的操作使用block或者GCD的时候。这些任务可以互相依赖并且无缝连接。
在连续执行多个任务时无需创建嵌套的“金字塔”的代码,你会只使用回调。
BFTasks是完全可组合的,允许您执行分支,并行和复杂的错误处理,而无需命名很多回调的意大利面条式(意指包含复杂庞大控制结构,杂乱无章的代码,特别是大量使用goto语句的代码等等)代码。
你可以安排基于任务的代码的顺序来执行,而不必使用分散的回调函数分开你的逻辑。
对于本文档中的例子,假设有异步版本的一些常用的解析方法,称为saveAsync:和findAsync:它返回一个Task。在后面的章节中,我们将介绍如何将自己定义这些功能。
The continueWithBlock Method
每个BFTask都有一个方法continueWithBlock: 包含有一个延时的block。当任务完成后,这个block将会执行。然后,您可以检查任务是否成功,并获得其结果。
1 |
|
BFTasks使用Objective-C的block,所以语法应该是非常简单的。让我们来看看一个例子。
1 |
|
在很多情况下,你希望做更多的工作,如果之前的任务并没有传递任何错误或取消操作,那么它将会走成功的回调。要做到这一点,使用continueWithSuccessBlock:方法,而不是continueWithBlock :
1 |
|
Chaining Tasks Together
BFTasks是有点不可思议,因为它们允许你把它们链接起来而无需嵌套。如果从continueWithBlock返回BFTask : 然后continueWithBlock返回的任务将不被视为完成,直到新任务的新的连续的block返回之后。这使您可以避免金字塔式代码,你将获得回调与执行多个操作。同样,你可以从continueWithSuccessBlock返回BFTask:。因此,返回的BFTask将做更多的异步工作。
1 |
|
Error Handling(错误处理)
通过仔细选择是否调用continueWithBlock:或continueWithSuccessBlock :,可以控制应用程序中的错误传播。使用continueWithBlock:您可以通过他们来处理错误信息。你可以把失败的任务当作抛出异常。事实上,如果你在continue回调时抛出(返回)一个异常,由此产生的后续任务将会产生故障与异常。
1 |
|
这是很方便的,在最后始终保持成功的一个回调链,而且只有唯一一个错误处理程序。
Creating Tasks(创建任务)
当你入门之后,你可以使用来自像findAsync方法返回的任务:或saveAsync : 然而,对于更高级的方案,你可能想使用自己的任务。要做到这一点,您可以创建一个BFTaskCompletionSource。这个对象可以让你创建一个新的BFTask,并控制其是否被标记为已完成或取消。创建BFTask后,您将需要调用的setResult : setError:或取消触发它的continuations。有了这些工具,你会很容易使自己的异步函数返回任务。例如,你可以使用fetchAsync的基于任务的版本:
1 |
|
同样地,很容易创建saveAsync : findAsync:或deleteAsync :
Tasks in Series(系列任务)
BFTasks很方便,当你想要执行一个串行的任务,每个任务都会等待上一个任务完成。例如,假设你要删除你的博客上所有的评论。
1 |
|
Tasks in Parallel(并行任务)
您还可以同时执行多个任务,使用taskForCompletionOfAllTasks:方法。您可以一次启动多个作业,并且,使用taskForCompletionOfAllTasks:将会创建一个新的被标记完成的任务,当所有的任务都完成时。只有当所有传入的任务取得成功,新的任务才算是成功。并行执行的操作会比串行时更快,但可能会消耗更多的系统资源和带宽。
1 |
|
Task Executors(任务执行器)
无论continueWithBlock:和continueWithSuccessBlock:这些方法都有另一种形式,采用BFExecutor的一个实例。这些都是continueWithExecutor:withBlock:和continueWithExecutor:withSuccessBlock : 这些方法允许你控制延续事件是如何执行的。默认的执行器将会使用GCD,但你可以提供自己的执行器分派工作到不同的线程。例如,如果你想继续在UI线程上工作:
1 |
|
Task Cancellation(取消任务)
这不是一个好的设计:用来跟踪BFTaskCompletionSource的注销与否。一个更好的模型是建立一个“取消标记”的顶层,并传递给你想做相同的“取消操作”的一部分,为每个异步函数。然后,在你的continuation blocks中,你可以检查取消标记是否已被取消,如果取消则返回[BFTask cancelledTask].例如:
1 |
|
注:取消标记实现应该是线程安全的。我们很可能会…像这样加入一些概念到Bolts在将来的某个时候。