2012-04-05 47 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是通過使用異步方法(其將創建爲每個請求後臺線程)一旦反饋已被髮送,它具有NSRunner在下一週期執行該方法,以避免重複之前除去數據提交。

通過使用異步,循環(主線程)將繼續,而不等待來自服務器的響應運行。在某些情況下(可能是大多數情況),循環將在每個請求的服務器的所有響應返回之前結束運行。如果是這樣,完成模塊的代碼將執行removeObjectsInArray這將導致發送的數據保留在self.feedbackQueue

後,我敢肯定,有幾種方法來避免這個問題。不過,我能想到的只有一個是用同步方法,而不是讓所有的請求的響應都回來之前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開發。因此,將「調度」添加到今天的必讀列表中。謝謝:) – 2012-04-05 04:47:25

+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和相關的指南很不錯太。 – 2012-04-05 04:50:52

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

嘿,這個也很有趣。其實它有點類似於我對應用程序的其他部分所做的。感謝隊友,簡單但有用。 – 2012-04-05 05:12:12