Search code examples
iosobjective-c-blocksgrand-central-dispatchnsoperationnsoperationqueue

Waiting for multiple blocks to finish


I have those methods to retrieve some object information from the internet:

- (void)downloadAppInfo:(void(^)())success
                failure:(void(^)(NSError *error))failure;
- (void)getAvailableHosts:(void(^)())success
                  failure:(void(^)(NSError *error))failure;
- (void)getAvailableServices:(void(^)())success
                     failure:(void(^)(NSError *error))failure;
- (void)getAvailableActions:(void(^)())success
                    failure:(void(^)(NSError *error))failure;

The downloaded stuff gets stored in object properties, so that is why the success functions return nothing.

Now, I want to have one method like this:

- (void)syncEverything:(void(^)())success
               failure:(void(^)(NSError *error))failure;

Which does nothing else than calling all the methods above, and returning only after every single method has performed its success or failure block.

How can I do this?

Hint: I am aware that cascading the methods calls in each others success block would work. But this is neither 'clean' nor helpful when later implementations include further methods.

Attempts:

I tried running each of the calls in an NSOperation and adding those NSOperations to an NSOperationQueue followed by a "completion operation" which depends on every one of the preceding operations.

This won't work. Since the operations are considered completed even before their respective success/failure blocks return.

I also tried using dispatch_group. But it is not clear to me wether I am doing it the right way. Unfortunately, it is not working.


Solution

  • You were almost there, the problem is most likely to be that those methods are asynchronous, so you need an extra synchronization step. Just try with the following fix:

    for(Appliance *appliance in _mutAppliances) {
      dispatch_group_async(
         group,
         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           dispatch_semaphore_t sem = dispatch_semaphore_create( 0 );
    
           NSLog(@"Block START");
    
           [appliance downloadAppInfo:^{
              NSLog(@"Block SUCCESS");
                dispatch_semaphore_signal(sem);
           }
           failure:^(NSError *error){
             NSLog(@"Block FAILURE");
             dispatch_semaphore_signal(sem);
           }];
    
           dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    
           NSLog(@"Block END");
     });
    
     dispatch_group_notify(
       group,
       dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
         NSLog(@"FINAL block");
         success();
     });
    }