Dispatch_group_notify is called as many times as dispatch_group_leave is called in nested blocks

I have this logic below; they are basically 3 nested group sending blocks.

  • The first group ( group) will do 3 short asynchronous tasks (just download the data from the web service) and another asynchronous task: upload the unsynchronized records to the web service, delete the synchronized ones locally and finally load the records from the web service (first an array with identifiers and basic information, and then each of these entries).
  • The second group ( saveGroup) is part of a longer task. It will wait until all these requests for unsynchronized entries to the web service are complete.
  • The third ( downloadGroup) will wait for all requests to load one record into the service to complete.

Everything goes fine until the third dispatch group. As you can see, I get the identifiers and basic information for the records on the server, iterate over the array and call dispatch_group_enter using downloadGroup, and then run the HTTP request. The call dispatch_group_leaveis called when the request is completed. I see what dispatch_group_leavegets called for each request and finally dispatch_group_notifygets called so many times.

-(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 , , ( , , ..)?

+4
1

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 .

+8

Source: https://habr.com/ru/post/1616084/


All Articles