2012-04-05 2 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]; 
} 

Я использую NSRunLoop создать NSThread выполнить метод sendFeedback каждый период времени. Способ, которым я отправлял данные в API, - это использовать метод Asynchronous (который создаст фоновый поток для каждого запроса). После отправки обратной связи его необходимо удалить до того, как NSRunner выполнит этот метод в следующий период, чтобы избежать дублирования представления данных.

При использовании асинхронного цикла (основной поток) будет продолжать работать, не дожидаясь ответа от сервера. В некоторых случаях (возможно, в большинстве случаев) цикл завершается до того, как будет возвращен весь ответ от сервера каждого запроса. Если это так, код блока завершающего будет выполняться после того, как removeObjectsInArray, что приведет к отправленным данным остается в self.feedbackQueue

Я уверен, что есть несколько способов избежать этой проблемы. Но единственное, что я могу представить, это использовать вместо этого метод Synchronous, так что removeObjectsInArray не будет выполняться до того, как все ответы запроса вернутся (либо успех, либо неудача). Но если я это сделаю, значит, интернет-соединение должно быть доступно на более длительный период. Время, необходимое для потока sendFeedback, будет больше. Даже он будет запускаться только что созданным NSThread, который не заставит приложение не отвечать, в любом случае понадобятся ресурсы.

Итак, есть ли какой-либо другой способ помимо того, что я упомянул выше? Любое предложение приветствуется.

спасибо.

ответ

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); 
    }); 
} 
+0

Это интересно, я новичок в Objective-C и iOS Dev. Итак, добавьте «рассылку» в список сегодняшнего обязательного чтения. Спасибо:) –

+1

@ Tar_Tw45 Да, Grand Central Dispatch - супер полезный API для многих вещей. Документация находится здесь: https://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079 и связанный с ней справочник довольно хорош слишком. –

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 
} 
+0

Эй, это тоже интересно. На самом деле это похоже на то, что я сделал с другой частью приложения. Спасибо, помощник, простой, но полезный. –