1

私はこれを実際よりも難しくしているに違いありません...または、オンラインで見たソリューションを間違って実装しています。

ループして結果を順番に辞書または配列にプッシュしたい URL の配列があります。次のリクエストを実行する前に辞書が更新されるのを待つにはどうすればよいですか? 基本的に、バックグラウンド スレッドで同期的に呼び出しを行いたいと考えています。

これが私がダウンロードを呼び出す場所です:

for (NSString *path in paths) {

    NSURLSession *session = [NSURLSession sessionWithConfiguration [NSURLSessionConfiguration defaultSessionConfiguration]];

    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
                                              cachePolicy:NSURLRequestUseProtocolCachePolicy
                                          timeoutInterval:10];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error)
                                                {

                                                }
                                                else
                                                {
                                                    NSError *parsingError = nil;
                                                    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
                                                                                                         options:NSJSONReadingAllowFragments
                                                                                                           error:&error];
                                                    if (parsingError)
                                                    {

                                                    }
                                                    else
                                                    {
                                                        [myDictionary addObject:dict];
                                                    }
                                                }
                                            }];
    [task resume];
}
4

1 に答える 1

2

1 つの要求が発行される前に前の要求の結果を実際に必要とする場合を除き (ここではそうではありません)、それらを順番に実行しないでください。シーケンシャルに発行する方が論理的に思えるかもしれませんが、そうするとパフォーマンスが大幅に低下します。それらを同時に発行し、結果を何らかの順序付けられていない構造 (辞書など) に保存し、すべてが完了したら、順序付けられた構造を構築します。

NSMutableDictionary *results = [NSMutableDictionary dictionaryWithCapacity:[paths count]];

// don't create new session for each request ... initialize this outside of the loop

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // or since you're not doing anything special here, use `sharedSession`

// since we're going to block a thread in the process of controlling the degree of 
// concurrency, let's do this on a background queue; we're still blocking
// a GCD worker thread as these run (which isn't ideal), but we're only using
// one worker thread.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // only do for requests at a time, so create queue a semaphore with set count

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(4); // only do four requests at a time

    // let's keep track of when they're all done

    dispatch_group_t group = dispatch_group_create();

    // now let's loop through issuing the requests

    for (NSString *path in paths) {
        dispatch_group_enter(group);                               // tell the group that we're starting another request

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait for one of the four slots to open up

        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
                                                      cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                  timeoutInterval:10];

        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) {
                // ...
            } else {
                NSError *parsingError = nil;
                NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                if (parsingError) {

                } else {
                    // synchronize updating of dictionary

                    dispatch_async(dispatch_get_main_queue(), ^{
                        results[path] = dict;
                    });
                }
            }

            dispatch_semaphore_signal(semaphore);                  // when done, flag task as complete so one of the waiting ones can start
            dispatch_group_leave(group);                           // tell the group that we're done
        }];
        [task resume];
    }

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // trigger whatever you want when they're all done

        // and if you want them in order, iterate through the paths and pull out the appropriate result in order
        for (NSString *path in paths) {
            // do something with `results[path]`
        }
    });
});

ここで余分な依存関係の量を減らそうとしたので、ディスパッチ グループとセマフォを使用しました。上記では、セマフォを使用して同時実行の程度を制限し、ディスパッチ グループを使用してすべての処理がいつ完了したかを識別しています。

個人的には、セマフォとグループは使用せず、これらの要求を非同期NSOperationサブクラスにラップしますが、コードに加えた変更を制限しようとしていました。ただし、NSOperation考え方は論理的には上記と同じです。それらを同時に実行しますが、タイムアウトにならないように同時実行の程度を制限し、すべての結果が取得された場合にのみ結果の取得をトリガーします。

于 2016-06-28T22:27:23.313 に答える