同じことを達成したかったのですが、ブロックを使用してコードを簡素化し、再帰メソッドを介してパラメーターを渡すという面倒な作業を行う必要はありませんでした。この NSArray カテゴリを思いつきました:
NS_ASSUME_NONNULL_BEGIN
@interface NSArray(MH)
- (void)mh_asyncEnumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL* stop, dispatch_block_t next))block;
@end
NS_ASSUME_NONNULL_END
@implementation NSArray(MH)
- (void)mh_asyncEnumerateObjectsUsingBlock:(void (^)(id _Nonnull obj, NSUInteger idx, BOOL* stop, dispatch_block_t next))block{
__block NSUInteger index = 0;
__block BOOL stop = NO;
void (^next)();
__block __weak typeof(next) weakNext;
weakNext = next = ^void() {
void (^strongNext)() = weakNext;
// check if finished
if(stop || index == self.count){
return;
}
id obj = self[index];
index++;
block(obj, index - 1, &stop, strongNext);
};
next();
}
@end
次のように使用されます。
NSArray* a = @[@"Malc", @"Bob", @"Jim"];
[a mh_asyncEnumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *stop, dispatch_block_t next) {
// simulate an async network call
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", obj);
next();
});
}];
出力:
2016-01-04 22:41:04.631 Malc
2016-01-04 22:41:05.632 Bob
2016-01-04 22:41:06.720 Jim
ご覧のとおり、このデモは配列内の各文字列を 1 秒の遅延で出力します。ブロック内でネットワーク呼び出しを行い、それが完了したら next を呼び出すことで使用できます。エラーが発生してキャンセルしたい場合は、 *stop = YES; を設定してください。next() を呼び出す前に、通常の列挙と同じようにします。
NSArray *sites = [self.fetchedResultsController.fetchedObjects copy];
NSLog(@"sites - %@",sites);
[sites mh_asyncEnumerateObjectsUsingBlock:^(Site *site, NSUInteger idx, BOOL *stop, dispatch_block_t next){
NSLog(@"site name - %@,",site.name);
[[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{
if(error){ // your completion block should have an error param!!!
*stop = YES;
}
NSLog(@"site name - %@",site.name);
next();
}];
}];