1

反復中に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];
}

NSRunLoop を使用して NSThread を作成し、一定期間ごとに sendFeedback メソッドを実行しています。APIにデータを送信する方法は、非同期メソッドを使用することです(リクエストごとにバックグラウンドスレッドを作成します)フィードバックが送信されたら、NSRunnerが次の期間にこのメソッドを実行する前に削除する必要があります。 .

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

その問題を回避する方法はいくつかあると確信しています。しかし、私が考えることができる唯一の方法は、代わりにSynchronousメソッドを使用して、すべてのリクエストの応答が返される前にremoveObjectsInArrayが実行されないようにすることです (成功または失敗のいずれか)。しかし、そうすると、インターネット接続を長期間利用できる必要があります。sendFeedback のスレッドに必要な時間が長くなります。新しく作成された NSThread によって実行されても、アプリが応答しなくなることはありませんが、とにかくリソースが必要になります。

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

ありがとうございました。

4

2 に答える 2

4

この種の問題に対処する方法はいくつかあります。ディスパッチグループを使用してフィードバックを同期し、インスタンス変数を使用して、進行中の新しいフィードバックバッチを実行しないようにすることをお勧めします。この例では、クラスに名前が付けられたインスタンス変数を作成すると仮定します。次のようにメソッド_feedbackUploadInProgressを書き直すことができます。-sendFeedback

- (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);
  });
}
于 2012-04-05T04:37:26.413 に答える
1

1 つのアプローチは、進行中のリクエストを追跡し、すべてのリクエストが完了したらキューをクリーンアップすることです。単純なアプローチでは保持サイクルが生成されるため、ブロックを追跡するのは少し難しいです。やるべきことは次のとおりです。

- (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
}
于 2012-04-05T04:54:05.833 に答える