dispatch_group_notify 被调用的次数与 dispatch_group_leave 在嵌套块中的调用次数相同

dispatch_group_notify being called as many times as dispatch_group_leave is called in nested blocks

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

  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 的任何建议,或者更好的是,如何以更清晰的方式实现此嵌套异步任务完成依赖性(意味着如何等待多个请求完成,然后触发一些更多并再次等待等等)?

您每次进入时都会通知 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 移出循环。