您可以按以下方式处理问题:
首先定义一个通用的完成块类型。注意,参数结果可以是任何东西。如果它是一个,NSError
这表示失败,否则成功。
typedef void (^completion_t)(id result);
为您的异步任务定义块类型。您的任务将索引作为输入,并且 - 由于它是异步的,因此它将完成块作为最后一个参数:
typedef void (^unary_async_t)(int index, completion_t completion);
您的任务块对象可以定义如下:
unary_async_t task = ^(int index, completion_t completion) {
[self loadDataAtIndex:index withCompletion:^(id result){
if (![result isKindOfClass:[NSError class]]) {
[self writeDataToFile:result];
result = @"OK";
}
if (completion) {
completion(result);
}
}];
};
注意:你loadDataAtIndex:withCompletion:
是异步的,因此它需要一个完成块作为最后一个参数。完成块的参数结果是异步任务的结果,即“页面数组” -NSError
如果失败,则为对象。
在此完成块中,您将结果(页面)安全地保存到磁盘,调用writeDataToFile:
. (我们假设这不会失败)。如果这一切都完成了,任务调用提供的完成块完成(如果不是 nil)传递整个操作的结果,如果成功,则为 @"OK",如果NSError
失败,则为。
现在,更有趣的部分:如何在一个循环中进行,其中许多 _task_s 将依次执行,一个接一个:
我们定义了两个辅助方法 - 或函数:
我们最终需要的是一个带有这个签名的函数:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion);
这是调用任务N 次的函数,将参数index传递给从lowerBound(包括)到upperBound(不包括)的范围,其中N等于upperBound - lowerBound并且index以lowerBound开头。
它是一个异步函数,因此将完成块作为最后一个参数。我重复自己吗?好吧,你应该认出这个模式!;)
这是实现:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion)
{
do_apply(lowerBound, upperBound, task, completion);
}
而另一个助手 - 最终执行某种“ for_each index in range[upperBound, lowerBound] 顺序调用带有参数index的任务”:
static void do_apply(int index, int upperBound,
unary_async_t task, completion_t completion)
{
if (index >= upperBound) {
if (completion)
completion(@"OK");
return;
}
task(index, ^(id result) {
if (![result isKindOfClass:[NSError class]]) {
do_apply(index + 1, upperBound, task, completion);
}
else {
// error occurred: stop iterating and signal error
if (completion) {
completion(result);
}
}
});
}
该函数do_apply
首先检查索引是否超出范围。如果,则完成并使用@“OK”调用完成处理程序。否则,它使用参数索引调用任务并提供一个完成处理程序,该处理程序在任务完成时被调用,它本身传递任务的结果。如果成功,将调用自身,参数索引加一。这可能看起来像“递归” - 但事实并非如此。已经返回,当它调用自己时。do_apply
do_apply
如果任务返回并且错误,则停止,从其完成处理程序中的任务do_apply
“返回”错误(最终由调用站点提供)。
现在,您只需将这些部分放在您的项目中 - 这应该相当容易。