2015-11-16 29 views
4

我有下面的邏輯;他們基本上是3個嵌套的調度組塊。dispatch_group_notify被調用多次,因爲在嵌套塊中調用dispatch_group_leave

  1. 第一組(group)將執行3個短異步任務(只是從Web服務下載數據)和1項較長的異步任務:上傳不同步的記錄,以網絡服務,本地刪除同步的人,最後下載來自Web服務的記錄(首先是包含ID和基本信息的數組,然後是每個記錄)。
  2. 第二組(saveGroup)是較長任務的一部分。它將等待,直到所有這些未記錄的對Web服務的記錄請求都完成。
  3. 第三個(downloadGroup)將一直等到所有這些單個記錄下載請求到服務完成。

一切都很好,直到第三派遣組。正如您所看到的,我可以獲取服務器上記錄的ID和基本信息,循環訪問數組並使用downloadGroup調用dispatch_group_enter,然後觸發HTTP請求。當請求完成時調用dispatch_group_leave。我可以看到每個請求都會調用dispatch_group_leave,並最終調用dispatch_group_notify多次。

-(void) doTheSync { 
    dispatch_group_t group = dispatch_group_create(); 

    [self syncFirstDataWithGroup: group]; 
    [self syncSecondDataWithGroup: group]; 
    [self syncThirdDataWithGroup: group]; 

    [self syncRecordsWithExternalGroup: group]; 

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
     NSLog(@"Finished all the sync"); 
    }); 
} 

-(void) syncRecordsWithExternalGroup: (dispatch_group_t) externalGroup { 
    dispatch_group_enter(externalGroup); 

    NSError* error = nil; 
    ConfigureDataModelHandler* configDataHandler = [ConfigureDataModelHandler sharedCoredata]; 
    WebserviceLib* RESTClient = [WebserviceLib sharedInstance]; 

    //get all unsynced records (f 
    NSArray* recordsUnsynced = [configDataHandler getAllRecordsWithSynced: NO ignoreDelete: YES withError: &error]; 

    if (!error) { 
     //upload them to the BE (and mark as synced if succeed 

     dispatch_group_t saveGroup = dispatch_group_create(); 

     //get full record dictionary, save and mark as synced 
     for (CMrecord* record in recordsUnsynced) { 
      NSDictionary* recordDict = [configDataHandler exportToDictionary: record.recordID.integerValue]; 

      dispatch_group_enter(saveGroup); 

      [RESTClient saverecord: recordDict onComplete:^(AFHTTPRequestOperation *operation, id responseObject) { 
       NSLog(@"Saved unsynced record (%@) to BE", record.recordID); 

       //mark as synced 
       [configDataHandler markrecordAsSynced: record.recordID.integerValue]; 
       dispatch_group_leave(saveGroup); 
      } onError:^(AFHTTPRequestOperation *operation, NSError *error) { 
       NSLog(@"Error saving unsynced record to BE %@", error); 
       dispatch_group_leave(saveGroup); 
      }]; 
     } 

     //** NOTIFY FINISH SAVING ** 
     dispatch_group_notify(saveGroup, dispatch_get_main_queue(), ^{ 
      NSLog(@"Finished saving all unsynced records to BE"); 

      //delete all synced records 
      //TODO: Check if this makes sense. Probably better to not delete anything until we got the full record from the BE... 
      [configDataHandler deleteRecordsSynced]; 

      //download small records from BE 
      NSString* agentNationalID = [self.coreData getLoginStatus].nationalID; 
      [RESTClient getRecordsForAgent: agentNationalID onComplete:^(NSInteger completeCode, NSArray *responseArray) { 
       NSLog(@"Success getting the records %@", responseArray); 

       if (completeCode == 200) { 
        dispatch_group_t downloadGroup = dispatch_group_create(); 

        for (NSDictionary* shortDict in responseArray) { 
          dispatch_group_enter(downloadGroup); 

          //download full records from BE 
          [RESTClient getRecordByCodeAndTimestamp: shortDict onComplete:^(NSInteger completeCode, NSDictionary *responseDictionary) { 
           NSLog(@"Success Downloading record"); 
           dispatch_group_leave(downloadGroup); 
          } onError:^(AFHTTPRequestOperation *operation, NSError *error) { 
           NSLog(@"Error downloading record %@", shortDict); 
           dispatch_group_leave(downloadGroup); 
          }]; 

          //** NOTIFY FINISH DOWNLOADING ** 
          dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ 
           NSLog(@"Finished downloading all the records"); 

           //This is CRASHING because this block is being called as many times as the dispatch_group_leave(downloadGroup) is called 
           dispatch_group_leave(externalGroup); 
          }); 
         } 
       } else { 
        NSLog(@"Error getting the records for Agent %@, with Code %li", @"AGENT ID", (long)completeCode); 
       } 
      } onError:^(AFHTTPRequestOperation *operation, NSError *error) { 
       NSLog(@"Error getting the records for Agent %@, %@", @"AGENT ID", operation.response); 
      }]; 
     }); 
    } 
} 

我用在其他地方調度組,一個正常的行爲(創建,回車,回車,離開,離開,通知),所以我不明白是怎麼回事。這與嵌套塊有關嗎?任何關於如何在完成時調用dispatch_group_notify的建議,或者更好的辦法是如何以更清晰的方式實現這種嵌套的異步任務完成依賴(意思是如何等待多個請求完成,然後再次啓動並再次等待等等)?

+0

我忘了補充說,在失敗的downloadGroup中80個HTTP請求被同時觸發,所以這也可能是問題所在。不知道這些隊列是否對併發任務的數量有限制...... – momo

+0

嗯,我剛剛意識到那個downloadGroup中的任務正在同步執行。在調用先前的dispatch_group_leave(downloadGroup)之前,不會執行下一個dispatch_group_enter(downloadGroup)。任何想法爲什麼發生這種情況? – momo

回答

8

你每次你進入它

for (NSDictionary* shortDict in responseArray) { 
    dispatch_group_enter(downloadGroup); 

    //download full records from BE 
    [RESTClient getRecordByCodeAndTimestamp: shortDict onComplete:^(NSInteger completeCode, NSDictionary *responseDictionary) { 
     NSLog(@"Success Downloading record"); 
     dispatch_group_leave(downloadGroup); 
    } onError:^(AFHTTPRequestOperation *operation, NSError *error) { 
     NSLog(@"Error downloading record %@", shortDict); 
     dispatch_group_leave(downloadGroup); 
    }]; 

    // BUG IS HERE 
    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ 
     NSLog(@"Finished downloading all the records"); 

     dispatch_group_leave(externalGroup); 
    }); 
} 

// dispatch_group_notify should be moved HERE 

你應該只通知一次團時間通知downloadGroup組,移動dispatch_group_notify圈外。