dispatch_group_notify被调用多次,因为在嵌套块中调用dispatch_group_leave

时间:2015-11-16 15:39:28

标签: ios objective-c asynchronous grand-central-dispatch

我在下面有这个逻辑;它们基本上是3个嵌套的调度组块。

  1. 第一组(group)将执行3个短异步任务(仅从Web服务下载数据)和1个更长的异步任务:将未同步的记录上载到Web服务,在本地删除已同步的记录,最后下载来自Web服务的记录(首先是包含ID和基本信息的数组,然后是这些记录中的每一个)。
  2. 第二组(saveGroup)是较长任务的一部分。它将等待所有未完成的记录请求完成到Web服务。
  3. 第三个(downloadGroup)将等到所有这些单个记录下载请求到服务完成。
  4. 一切顺利,直到第三个派遣小组。如您所见,我获取服务器上记录的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一次的任何建议,或者更好的是,如何以更清洁的方式实现此嵌套异步任务完成依赖性(意味着如何等待多个请求完成,然后解雇一些更多,再等一下等等)?

1 个答案:

答案 0 :(得分:9)

您每次输入时都会通知downloadGroup

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

你应该只通知小组一次,将dispatch_group_notify移出循环。