简体   繁体   中英

How can I wait the finish of a block before executing the next one?

I'm having trouble with semaphore. I have a serie of blocks and I want a block is executed just when the previous one has been finished its work. I red that I have to play with gcd semaphore but the app stop working at the point signed in the code and it never enters in the block completation.

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

NSLog(@"1. AZIENDE: BEGIN");
[Model syncAziende:^(id response, NSError *error) {
    dispatch_semaphore_signal(semaphore);
    NSLog(@"2. AZIENDE: FINISH");
}];

/*BLOCKS HERE */dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

NSLog(@"3. AZIENDE: BEGIN");
[Model syncContatti:^(id response, NSError *error) {
    NSLog(@"4. AZIENDE: FINISH");
}];

Here's the output:

2014-03-26 09:35:56.561 NSalesCDC[1071:60b] 1. AZIENDE: BEGIN

Trying to use semaphores is not the correct approach to this. Instead, chain your callbacks together. You can create your blocks outside of each other to prevent horrible, pyramid-like callback hell.

This should work for you:

// The block that is called when syncContatti: is complete
void (^contattiBlock)(id, NSError *) = ^(id response, NSError *error) {
  NSLog(@"4. AZIENDE: FINISH");
};

// The block that is called when syncAziende: is complete
void (^aziendeBlock)(id, NSError *) = ^(id response, NSError *error) {
   NSLog(@"2. AZIENDE: FINISH");
   // Now, we know that syncAziende: is finished, we can start the next step
   [Model syncContatti:conCattiBlock];
};

// Now we begin the entire chain of events
NSLog(@"1. AZIENDE: BEGIN");
[Model syncAziende:aziendeBlock];

One downside of this is that you have to define your blocks in reverse-order, but that's not too bad.

You can use dispatch_barrier_async() . dispatch_barrier_async() will wait until all the tasks that are scheduled before the barrier to finish execution and then it will start execution. All the tasks scheduled after the barrier will wait for the barrier to finish.

dispatch_async(myQueue,
// this will start working now
});

dispatch_barrier_async(myQueue,
// this will wait until the previous block finish 
//and will block the next one from execution
})

dispatch_async(myQueue,
// this will wait for the barrier to finish
});

You may use NSOperation dependencies. Eg

NSOperationQueue * que = [[NSOperationQueue alloc] init];

NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"first");
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"second");
}];
[op2 addDependency:op];
[que addOperations:@[op,op2] waitUntilFinished:NO];

You can also call the second block within the first or use other guys approaches

If your reply to my comment above really is the structure of your code, it cries out for refactoring. The repetition is a good candidate for abstraction.

Perhaps something like:

static const struct {
    SEL selector;
    NSString* message;
} steps[] = {
    { @selector(syncAziende:), @"Sincrinizzo i contatti" }.
    { @selector(syncContatti:), @"Sincrinizzo le destinazioni" }.
    // ...
};

- (void) doStep:(int) step
{
    if (step < sizeof(steps) / sizeof(steps[0]))
    {
        [Model performSelector:steps[step].selector withObject:[^(id response, NSError *error){
            hud.labelText = [NSString stringWithFormat:@"%d/%d: %@", step + 1, sizeof(steps) / sizeof(steps[0]), steps[step].message];
            [self doStep:step + 1];
        } copy]];
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            hud.mode = MBProgressHUDModeText;
            hud.labelText = @"Sincronizzazione terminata";
            [hud hide:YES afterDelay:1.5];
        });
    }
}

...
    [self doStep:0];

Use it this way:

- (void) testSomethingAPI
{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    [Model syncAziende: ^(id response, NSError *error)
    {
        // Your Stuff here...

        dispatch_semaphore_signal(semaphore);
    }];

    while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
    {
        [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate dateWithTimeIntervalSinceNow: 1.f]];
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM