Search code examples
objective-cgrand-central-dispatch

dispatch_group_t - EXC_BAD_INSTRUCTION


This is my first time using dispatch_group_t and I'm making a simple mistake. dispatch_group_notify is getting called before any of the async calls are returning therefore before even the first dispatch_group_leave(group), which then throws a EXC_BAD_INSTRUCTION.

However, I don't know why this is happening.

dispatch_queue_t queue =      dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    [self getCategoriesMainWithParameters:nil withSuccess:^(NSArray *objects) {
        [categoriesMain addObjectsFromArray:objects];
        dispatch_group_leave(group);
    } failure:^(NSError *error) {
        groupError = error;
        dispatch_group_leave(group);
    }];
});

dispatch_group_async(group, queue, ^{
    [self getCategoriesSubWithParameters:nil withSuccess:^(NSArray *objects) {
        [categoriesSub addObjectsFromArray:objects];
        dispatch_group_leave(group);
    } failure:^(NSError *error) {
        groupError = error;
        dispatch_group_leave(group);
    }];
});

dispatch_group_async(group, queue, ^{
    [self getCategoriesProductWithParameters:nil withSuccess:^(NSArray *objects) {
        [categoriesProduct addObjectsFromArray:objects];
        dispatch_group_leave(group);
    } failure:^(NSError *error) {
        groupError = error;
        dispatch_group_leave(group);
    }];
});

dispatch_group_notify(group,dispatch_get_main_queue(),^{
   ...
});

Solution

  • The function dispatch_group_async automatically “enters” the group when the block is dispatched, and automatically “leaves” when the dispatched block finishes. But you are calling dispatch_group_leave again, so you end up with too many “leave” calls, resulting in the error you describe.

    One should either call dispatch_group_async (with all the “enter” and “leave” calls made for you), or manually call dispatch_group_enter before the async call and calling dispatch_group_leave in the completion handler of the asynchronous process.

    In this case, because you are calling an asynchronous task inside the dispatched block, the dispatch_group_async pattern won’t work. You want to manually dispatch_group_enter before calling the asynchronous method and then having the completion handler call dispatch_group_leave, as you have. The group will be notified when every “enter” is matched by a “leave”. And because the method that you are calling is already asynchronous, you don’t need to use dispatch_async or dispatch_group_async at all.

    Thus, it might look like:

    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    [self getCategoriesMainWithParameters:nil withSuccess:^(NSArray *objects) {
        [categoriesMain addObjectsFromArray:objects];
        dispatch_group_leave(group);
    } failure:^(NSError *error) {
        groupError = error;
        dispatch_group_leave(group);
    }];
    
    dispatch_group_enter(group);
    [self getCategoriesSubWithParameters:nil withSuccess:^(NSArray *objects) {
        [categoriesSub addObjectsFromArray:objects];
        dispatch_group_leave(group);
    } failure:^(NSError *error) {
        groupError = error;
        dispatch_group_leave(group);
    }];
    
    dispatch_group_enter(group);
    [self getCategoriesProductWithParameters:nil withSuccess:^(NSArray *objects) {
        [categoriesProduct addObjectsFromArray:objects];
        dispatch_group_leave(group);
    } failure:^(NSError *error) {
        groupError = error;
        dispatch_group_leave(group);
    }];
    
    dispatch_group_notify(group,dispatch_get_main_queue(),^{
        ...
    });