次のように問題にアプローチできます。
最初に汎用完了ブロック型を定義します。パラメータの結果は何でもよいことに注意してください。これが の場合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 が順番に次々に実行されるループでこれを作成する方法:
2 つのヘルパー メソッドまたは関数を定義します。
最後に必要なのは、次のシグネチャを持つ関数です。
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion);
これは、タスクをN 回呼び出す関数であり、 lowerBound (含む) からupperBound (含まない) までの範囲のパラメーターインデックスを渡します。ここで、 NはupperBound - lowerBoundに等しく、インデックスはlowerBoundで始まります。
これは非同期関数であるため、最後のパラメーターとして完了ブロックを受け取ります。繰り返しますか?さて、あなたはパターンを認識する必要があります!;)
実装は次のとおりです。
void apply_each_with_range(int lowerBound, int upperBound,
unary_async_t task, completion_t completion)
{
do_apply(lowerBound, upperBound, task, completion);
}
そして、もう 1 つのヘルパー - 最終的に何らかの " for_each index in range[upperBound, lowerBound] パラメーターインデックスを使用してタスクを順次呼び出します"を実行します。
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を呼び出し、タスクの終了時に呼び出される完了ハンドラーを提供します。この完了ハンドラー自体がタスクの結果を渡します。成功した場合は、引数のインデックスを 1 つ増やして自身を呼び出します。これは「再帰」のように見えるかもしれませんが、そうではありません。それ自体を呼び出すときに、すでに返されています。do_apply
do_apply
タスクが返されてエラーが発生した場合は停止し、完了ハンドラー (最終的に呼び出しサイトによって提供される) でタスクdo_apply
からエラーを「返します」 。
あとは、これらのピースをプロジェクトにまとめるだけです。これはかなり簡単なはずです。