Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C – Waiting for two async methods to complete

I'm calling four methods that I want to execute in synchronous order, the first two methods are synchronous, the last two methods are asynchronous (data fetching from URLs).

Pseudo-code:

- (void)syncData {

    // Show activity indicator
    [object sync]; // Synchronous method
    [object2 sync]; // Synchronous method
    BOOL object3Synced = [object3 sync]; // Async method
    BOOL object4Synced = [object4 sync]; // Async method
    // Wait for object3 and object4 has finished and then hide activity indicator
}

How can I achieve this?

like image 276
Peter Warbo Avatar asked Nov 30 '12 20:11

Peter Warbo


3 Answers

Use a barrier:

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

Submits a barrier block for asynchronous execution and returns immediately.

When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the queue executes the barrier block by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.

This example outputs 1 2 3 4 done although being asynchronous, it could be 1 2 4 3 done. Since I understand you want to handle an activity indicator, this shouldn't matter.

#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        dispatch_queue_t queue = dispatch_queue_create("com.myqueue", 0);
        dispatch_sync(queue,  ^(){NSLog(@"1");} );
        dispatch_sync(queue,  ^(){NSLog(@"2");});
        dispatch_async(queue, ^(){NSLog(@"3");});
        dispatch_async(queue, ^(){NSLog(@"4");});
        dispatch_barrier_sync(queue, ^(){NSLog(@"done");});
    }
}

For other ways to test asynchronous code, see: https://stackoverflow.com/a/11179523/412916

like image 140
Jano Avatar answered Nov 18 '22 09:11

Jano


Assuming you actually have some sort of way of knowing when the asynchronous methods are done, what you probably want is something like:

- (void)syncData {

    // Show activity indicator
    [object sync]; // Synchronous method
    [object2 sync]; // Synchronous method

    _object3Synced = _object4Synced = NO;
    [object3 syncWithCompletionHandler:
          ^{
               _object3Synced = YES;
               [self considerHidingActivityIndicator];
          }]; // Async method
    [object4 syncWithCompletionHandler:
          ^{
               _object4Synced = YES;
               [self considerHidingActivityIndicator];
          }]; // Async method
}

- (void)considerHidingActivityIndicator
{
     if(_object3Synced && _object4Synced)
     {
         // hide activity indicator, etc
     }
}
like image 25
Tommy Avatar answered Nov 18 '22 08:11

Tommy


You can make a subclass of UIActivityInidicator, add an activityCount property and implement these two additional methods:

- (void)incrementActivityCount
{
    if(_activityCount == 0)
    {
        [self startAnimating];
    }
    _activityCount++;
}

- (void)decrementActivityCount
{
    _activityCount--;
    if(_activityCount <= 0)
    {
        _activityCount = 0;
        [self stopAnimating];
    }
}

Now whenever you start something that uses the activity counter call incrementActivityCount and in its completion block (or otherwise when it finishes) call decrementActivityCount. You can do other things if you want in these methods, the above is just a simple example which is probably sufficient in most cases (especially if you set hidesWhenStopped = YES).

like image 3
jhabbott Avatar answered Nov 18 '22 08:11

jhabbott