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

cocoa-touch, concurrency, grand-central-dispatch, ios, objective-c-blocks

Solution

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

Problem

I'm trying to synchronize the following code in iOS5: - an object has a method which makes an HTTP request from which it gets some data, including an URL to an image - once the data arrives, the textual data is used to populate a CoreData model - 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. - 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. - 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?

Original source