2016-06-28 2 views
1

私はこれをもっと難しくしなければなりません...私が誤ってオンラインに見ているソリューションを実装する必要があります。NSURLSessionDataTaskを使用したURLのXcodeループ配列

私はループしたい順番の配列を持っており、その結果を辞書または配列に順番にプッシュします。次のリクエストを実行する前に、辞書が更新されるまで待つことができますか?基本的には、バックグラウンドスレッドで同期して呼び出しを行いたい。私はダウンロードを呼び出すのはここ

は次のとおりです。

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]; 
} 

答えて

2

つの要求が実際に(ここではそうではありません)が発行される前に、前の要求の結果を必要としない限り、あなたはが順番に実行すべきではありません。連続して発行する方がより論理的だと感じるかもしれませんが、そうするには大きなパフォーマンス上のペナルティがあります。それらを同時に発行し、結果をいくつかの順序付けられていない構造(辞書のようなもの)に保存して、すべて完了したら順序構造を構築します。

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アイデアは論理的には上記と同じです:同時に実行しますが、同時実行の度合いを制限してタイムアウトにならないようにし、すべての結果が取得されたときだけ結果の取得をトリガーします。

+0

「NSOperationQueue」アプローチの手順については、http://stackoverflow.com/a/23837970/1271826またはhttp://stackoverflow.com/a/24943851/1271826を参照してください。 – Rob

+0

ありがとう@ロブ、私はセマフォを使用しようとしましたが、それは私のために働いていませんでした。もちろん、私は順番にそれをやろうとしていた...これは、私がそれらを発行した順序で記述したかったからです。私はあなたが提案したように後でそれらを注文することができます。 NSOpertionを試してみるかもしれません。 –

+0

セマフォは、正しく使用されないと問題(特にデッドロック)を引き起こす可能性があります。私は非同期 'NSOperation'のアプローチははるかにエレガントだと思うが、上記はうまくいくはずです。 – Rob

関連する問題