2

高速列挙を使用し、列挙ブロックでネットワーク要求を非同期に送信します。

そのため、enumerateObjectsUsingBlock が発生します。ブロックを超高速で呼び出して、しばらくしてから列挙ブロックを終了させます。

一部のリクエストは他のリクエストよりも早く終了するため、これは異なる結果につながります。だから、私が望んでいたようにソートされていません。

ブロックをフリーズするように設定し、非同期ネットワーク リクエストが完了した後、次のリクエストに進むように指示する方法はありますか?

ここにいくつかのコードがあります

    NSArray *sites = [self.fetchedResultsController.fetchedObjects copy];
    NSLog(@"sites - %@",sites);
    [sites enumerateObjectsUsingBlock:^(Sites *site, NSUInteger idx, BOOL *stop) {
        NSLog(@"site name - %@,",site.name);

        [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{

                NSLog(@"site name - %@",site.name);
        }];
    }];

ありがとう!

4

3 に答える 3

2

ブロックをフリーズするように設定し、非同期ネットワーク リクエストが完了した後、次のリクエストに進むように指示する方法はありますか?

NSArray *sites = [self.fetchedResultsController.fetchedObjects copy];
NSLog(@"sites - %@",sites);
[sites enumerateObjectsUsingBlock:^(Sites *site, NSUInteger idx, BOOL *stop) {
    NSLog(@"site name - %@,",site.name);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{

            NSLog(@"site name - %@",site.name);
dispatch_semaphore_signal(semaphore);
    }];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}];

^ これを行うには理想的な方法ではありませんが、非同期リクエストが完了するのを待ってから先に進む前に同期的に反復したい場合は、上記の方法で GCD を介して実行します。非同期タスクが完了した後にすべてのグループが残されるのを待っている間に、dispatch_group を繰り返してインクリメントできる他の方法があります。

dispatch_group_t downloadGroup = dispatch_group_create();


        dispatch_group_enter(downloadGroup);
        [self fetchStuffInBackground:background withCompletion:^(NSArray *stuff, NSError *error) {
            NSLog(@"leaving stuff");
            dispatch_group_leave(downloadGroup);
        }];
        dispatch_group_enter(downloadGroup);
        [self fetchAOtherStuffInBackground:background withCompletion:^(NSArray *stuff, NSError *error) {
            NSLog(@"leaving other stuff");
            dispatch_group_leave(downloadGroup);
        }];
              dispatch_group_enter(downloadGroup);
        [self fetchLastStuffInBackground:background withCompletion:^(NSArray *lastStuff, NSError *error) {
            NSLog(@"leaving last stuff");
            dispatch_group_leave(downloadGroup);
        }];      
    }

}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
    if (callback) {
        callback(error);
    }
});
于 2016-01-04T23:16:25.773 に答える
1

同じことを達成したかったのですが、ブロックを使用してコードを簡素化し、再帰メソッドを介してパラメーターを渡すという面倒な作業を行う必要はありませんでした。この 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();
     }];
 }];
于 2016-01-04T22:36:10.737 に答える
0

ブロックをフリーズするように設定し、非同期ネットワーク リクエストが完了した後、次のリクエストに進むように指示する方法はありますか?

コードを再編成することで、その結果を得ることができます。列挙を使用する代わりに、完了ブロックから非同期リクエストを 1 つずつ実行するだけです。

- (void) doRequestAsync:(NSArray*)sites index:(NSUInteger)index {

    if (index >= [sites count]) return;

    NSString* site = [sites objectAtIndex:index];
    [[Wrapper sharedWrapper] sendRequestTo:site completionBlock:{
            NSLog(@"site name - %@",site.name);
            [self doRequestAsync:sites index:++index];
    }];

}

これに代わる方法Wrapperは、非同期ネットワークを使用するようにクラスを変更することです (ただし、UI のブロックを回避するために、セカンダリ スレッドで使用します)。

または、 Async Completion Token パターンを実装して、受信時に応答を並べ替えることができるようにすることもできます。

于 2013-01-12T10:17:12.073 に答える