4

I'm executing an online fetch of data in a thread and I want to do something immediately after the block is executed.

Here's my code:

- (IBAction)refresh:(UIBarButtonItem *)sender {
    NSLog(@"checking");
    [self editToolbar];
    dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
    dispatch_async(fetchQ, ^{
        [self setupFetchedResultsController];
        [self fetchImonggoItemsDataIntoDocument: self.itemDatabase];
    });
    dispatch_release(fetchQ); 

    NSLog(@"done checking");
    //do something here
}

The thing is dispatch_async returns immediately and "done checking" prints immediately even before the block is done executing. How do I solve this?

acecapades
  • 893
  • 12
  • 23
  • 3
    Why don't you put whatever it is you want to do at the end of the Block? Am I misunderstanding your question? – jscs Mar 01 '12 at 03:35
  • 1
    Is the problem that `performBlock:` returns right away (runs asynchronously)? There's always `performBlockAndWait:`, as long as it's on a background thread. – gregheo Mar 01 '12 at 04:04
  • @acecapades i think you need to debug your code and after that get what is the exact point from where your control returns !! – aks.knit1108 Mar 04 '12 at 10:57
  • @acecapades try these links http://stackoverflow.com/questions/3379008/how-can-i-be-notified-when-a-dispatch-async-task-is-complete http://stackoverflow.com/questions/8668585/dispatch-sync-call-into-a-dispatch-async-call – aks.knit1108 Mar 04 '12 at 11:50
  • Try dispatch_sync in place of dispatch_async – aks.knit1108 Mar 04 '12 at 11:53
  • thanks @Amit I tried that, but it still didn't work. Thanks though – acecapades Mar 05 '12 at 01:24

2 Answers2

4

I think it's an architectural issue. The tasks are something like:

  1. edit toolbar
  2. fetchImonggoItemsDataIntoDocument
  3. do something else

If these must be done exactly in order then I don't quite understand the use of blocks or queues; just run the statements after each other and you'll be set.

Otherwise, alternative #1 would be to use dispatch_sync rather than dispatch_async. Again, I'm not quite sure what the benefit of using a queue would be but there it is.

Alternative #2 would be to use a callback from the block. Something like:

- (IBAction)refresh:(UIBarButtonItem *)sender {
    NSLog(@"checking");
    [self editToolbar];
    dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
    dispatch_async(fetchQ, ^{
        [self setupFetchedResultsController];
        [self fetchImonggoItemsDataIntoDocument: self.itemDatabase];
        [self doneChecking]; // <-- NOTE! call the callback
    });
    dispatch_release(fetchQ); 
}

// NOTE! refresh: has been split up into two methods
- (void)doneChecking:
    NSLog(@"done checking");
    //do something here
}
gregheo
  • 4,182
  • 2
  • 23
  • 22
  • It's also possible to enqueue another block to do the callback business, again using `dispatch_async`, but there's really no reason not to do it the way you've written here. (I don't even understand why this was a question...) – jscs Mar 05 '12 at 01:24
  • It's worth pointing out that if `doneChecking` is going to operate on any UI elements that it should be done in the main thread and not in fetchQ. Use another dispatch call but just request the main queue. – Joey J Apr 04 '13 at 06:09
1

As others have already suggested, this is probably what you need.

NSArray *items = [iMonggoFetcher fetchImonggoData:IMONGGO_GENERIC_URL_FOR_PRODUCTS withFormat:@"json" withDateRangeArgs:args];
[document.managedObjectContext performBlock:^{
    for (NSDictionary *itemInfo in items){
        [Product productWithImonggoInfo:itemInfo inManagedObjectContext:document.managedObjectContext];
    }
    // Put here what you need :)
}];
nacho4d
  • 43,720
  • 45
  • 157
  • 240