Ios – How should I use GCD dispatch_barrier_async in iOS (seems to execute before and not after other blocks)

cocoa-touchconcurrencygrand-central-dispatchiosobjective-c-blocks

I'm trying to synchronize the following code in iOS5:

  1. an object has a method which makes an HTTP request from which it
    gets some data, including an URL to an image
  2. once the data arrives, the textual data is used to populate a
    CoreData model
  3. at the same time, a second thread is dispatched async to download
    the image; this thread will signal via KVO to a viewController when
    the image is already cached and available in the CoreData model.
  4. since the image download will take a while, we immediately return
    the CoreData object which has all attributes but for the image to
    the caller.
  5. Also, when the second thread is done downloading, the CoreData model
    can be saved.

This is the (simplified) code:

- (void)insideSomeMethod
{
    [SomeHTTPRequest withCompletionHandler:
     ^(id retrievedData) 
     {
         if(!retrievedData)
         {
             handler(nil);
         }

         // Populate CoreData model with retrieved Data...

         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
             NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
             aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
         });

         handler(aCoreDataNSManagedObject);
         [self shouldCommitChangesToModel];
     }];
}

- (void)shouldCommitChangesToModel
{
    dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSError *error = nil;
        if(![managedObjectContext save:&error]) 
        {
            //  Handle error
        }  
    });
}

But what's going on is that the barrier-based save-block is always executed before the the image-loading block. That is,

dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSError *error = nil;
            if(![managedObjectContext save:&error]) 
            {
                //  Handle error
            }  
        });

Executes before:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                 NSURL* userImageURL = [NSURL URLWithString:[retrievedData valueForKey:@"imageURL"]];
                 aCoreDataNSManagedObject.profileImage = [NSData dataWithContentsOfURL:userImageURL];
             });

So obviously I'm not really dispatching the image-loading block before the barrier, or the barrier would wait until the image-loading block is done before executing (which was my intention).

What am I doing wrong? how do I make sure the image-loading block is enqueued before the barrier block?

Best Answer

At first glance the issue may be that you are dispatching the barrier block on a global concurrent queue. You can only use barrier blocks on your own custom concurrent queue. Per the GCD docs on dispatch_barrier_async, if you dispatch a block to a global queue, it will behave like a normal dispatch_async call.

Mike Ash has a good blog post on GCD barrier blocks: http://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

Good luck

T

Related Topic