2012-04-05 14 views
1

Best way to remove from NSMutableArray while iterating?によれば、NSMutableArrayから反復処理中にオブジェクトを削除することはできません。反復処理からオブジェクトを削除するNSMutableArray

しかし、私は次のよう

- (void)sendFeedback { 
    NSMutableArray *sentFeedback = [NSMutableArray array]; 
    for (NSMutableDictionary *feedback in self.feedbackQueue){ 
     NSURL *url = [NSURL URLWithString:@"someApiUrl"]; 
     ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; 
     [request setPostValue:[feedback objectForKey:@"data"] forKey:@"data"]; 
     [request setCompletionBlock:^{ 
      [sentFeedback addObject:feedback]; 
     }]; 
     [request startAsynchronous]; 
    } 
    [self.feedbackQueue removeObjectsInArray:sentFeedback]; 
} 

のようなコードを持っている場合、私はsendFeedback方法、時間の周期ごとに実行するためにNSThreadを作成するために、NSRunLoopを使用しています何。私がAPIにデータを送信する方法は、非同期メソッド(リクエストごとにバックグラウンドスレッドを作成する)を使用することです。フィードバックが送信されると、NSRunnerが次の期間にこのメソッドを実行して重複を避けるために、データ提出

非同期を使用すると、ループ(メインスレッド)はサーバーからの応答を待たずに実行を継続します。場合によっては(ほとんどの場合)、各要求のサーバーからのすべての応答が戻ってくる前に、ループは実行を終了します。そのようにした場合、完了ブロックのコードが送信されたデータになりますremoveObjectsInArrayが、私はその問題を回避するには、いくつかの方法があることをかなり確信してself.feedbackQueue

に残った後に実行されます。しかし、考えられる唯一の方法は、同期メソッドを代わりに使用して、removeObjectsInArrayがすべての要求の応答が返ってくる前に実行されないようにすることです(成功または失敗のいずれか)。しかし、私がそうするなら、それはインターネット接続がより長い期間利用可能でなければならないということです。 sendFeedbackのスレッドに必要な時間が長くなります。それが新しく作成されたNSThreadによって実行されても、アプリケーションが応答しなくても、リソースは必要になります。

私は上記以外の方法がありますか?どんな提案も大歓迎です。

ありがとうございます。

答えて

4

この種の問題に対処する方法はいくつかあります。ディスパッチグループを使用してフィードバックを同期させ、インスタンス変数を使用して、新しいフィードバックバッチを実行しないようにしておくことをお勧めします。

- (void)sendFeedback 
{ 
    if(_feedbackUploadInProgress) return; 
    _feedbackUploadInProgress = YES; 

    dispatch_group_t group = dispatch_group_create(); 
    NSMutableArray *sentFeedback = [NSMutableArray array]; 
    for (NSMutableDictionary *feedback in self.feedbackQueue) { 
    // enter the group for each item we're uploading 
    dispatch_group_enter(group); 
    NSURL *url = [NSURL URLWithString:@"someApiUrl"]; 
    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; 
    [request setPostValue:[feedback objectForKey:@"data"] forKey:@"data"]; 
    [request setCompletionBlock:^{ 
     [sentFeedback addObject:feedback]; 
     // signal the group each time we complete one of the feedback items 
     dispatch_group_leave(group); 
    }]; 
    [request startAsynchronous]; 
    } 
    // this next block will execute on the specified queue as soon as all the 
    // requests complete 
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
    [self.feedbackQueue removeObjectsInArray:sentFeedback]; 
    _feedbackUploadInProgress = NO; 
    dispatch_release(group); 
    }); 
} 
+0

これは興味深いことですが、私はObjective-CとiOS Devの新機能です。だから、今日の必読リストに「ディスパッチ」を追加してください。ありがとう:) –

+1

@ Tar_Tw45ええ、グランドセントラルディスパッチは、多くのもののための超便利なAPIです。ドキュメントはhttps://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079にあり、関連ガイドはかなり良いですあまりにも。 –

1

一つのアプローチが要求機内を追跡することであるとするとき、キューがクリーンアップを実行します。この例では、あなたは自分のクラスに_feedbackUploadInProgressを名前付きインスタンス変数を作成すると仮定しましょう、あなたはこのようなあなたの-sendFeedback方法を書き換えることができ彼らはすべて完了です。素朴なアプローチは保持サイクルを生成するため、ブロックを追跡することは少し難解です。ここで何をするのですか:

- (void)sendFeedback { 

    NSMutableArray *sentFeedback = [NSMutableArray array]; 

    // to keep track of requests 
    NSMutableArray *inflightRequests = [NSMutableArray array]; 

    for (NSMutableDictionary *feedback in self.feedbackQueue){ 
     NSURL *url = [NSURL URLWithString:@"someApiUrl"]; 

     ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; 

     // save it 
     [inflightRequests addObject:request]; 

     // this is the ugly part. but this way, you can safely refer 
     // to the request in it's block without generating a retain cycle 
     __unsafe_unretained ASIFormDataRequest *requestCopy = request; 

     [request setPostValue:[feedback objectForKey:@"data"] forKey:@"data"]; 
     [request setCompletionBlock:^{ 
      [sentFeedback addObject:feedback]; 

      // this one is done, remove it 
      // notice, since we refer to the request array here in the block, 
      // it gets retained by the block, so don't worry about it getting released 
      [inflightRequests removeObject:requestCopy]; 

      // are they all done? if so, cleanup 
      if (inflightRequests.count == 0) { 
       [self.feedbackQueue removeObjectsInArray:sentFeedback]; 
      } 
     }]; 
     [request startAsynchronous]; 
    } 
    // no cleanup here. you're right that it will run too soon here 
} 
+0

ねえ、これも面白いです。実際には、アプリの他の部分でやったのと似ています。おかげで、シンプルだが便利。 –

関連する問題