您可以按如下方式解决问题:
首先定义一个通用的完成块类型。注意,参数 result 可以是任何东西。如果是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: 是异步的,因此,它需要一个完成块作为最后一个参数。完成块的参数 result 是异步任务的结果,即“页面数组” - 如果失败,则为 NSError 对象。
在这个完成块中,您将结果(页面)保存到磁盘,调用writeDataToFile:。 (我们假设这不会失败)。如果这一切都完成了,task 调用提供的完成块 completion(如果不是 nil)传递整个操作的结果,如果成功则为 @"OK" , 和 NSError 以防失败。
现在,更有趣的部分是:如何在一个循环中进行,其中许多 _task_s 将一个接一个地依次执行:
我们定义了两个辅助方法 - 或函数:
我们最终需要的是一个带有这个签名的函数:
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion);
这是一个调用task N次的函数,传递给它的参数index在lowerBound(包括)到upperBound(不包括),其中 N 等于 upperBound - lowerBound 和 index 以 下界。
这是一个异步函数,因此将完成块作为最后一个参数。我重复自己吗?好吧,你应该认出这个模式! ;)
这里是实现:
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] 顺序调用 task 参数 索引":
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”调用完成处理程序。
否则,它使用参数 index 调用 task 并提供一个完成处理程序,该处理程序在 task 完成时被调用,它本身会传递任务的结果。如果成功,do_apply 将调用自身,参数索引加一。这可能看起来像“递归” - 但事实并非如此。 do_apply 在调用自身时已经返回。
如果 task 返回并出错,do_apply 停止,在其完成处理程序中“返回”来自 task 的错误(最终由调用站点提供) .
现在,您只需将这些部分放在您的项目中 - 这应该相当容易。