2

I'm running into this weird bug.. basically I got this blocK definition:

UILabel* filterButtonLabel;
void (^labelUpdater)(NSString *, id) = ^(NSString* newLabelText, id delegate){
    filterButtonLabel.text = newLabelText;   // command A   
    dispatch_after(DISPATCH_SECONDS_FROM_NOW(1), dispatch_get_main_queue(), ^{
        [delegate abortFilter];  //command B
    });
};

this block is called from the worker queue like so:

dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(taskQ, ^{
    [searchWorker withDelegate:self withUpdater:labelUpdater]; //<- worker thread in here
});

updater(@"filter applied", delegate);

(I know you'll ask: why the delegate? don't worry it just saves me a lot of trouble, and I believe it's irrelevant to this discussion).

The thing I noticed is that this command A only executes after command B is done.. the idea was to update the UI with some information for the user.. then dismiss the UI holding that label after 1 second (I wanted to give the user enough time to read the notification.. know what's going on.. then have the UI dismiss itself).

what's happening is that command A executes and the UI dismisses at the same time! if I take the whole dispatch after clause out.. command A executes as desired and that's it.

I also tried doing this:

__block UILabel* filterButtonLabel;
void (^labelUpdater)(NSString *, id) = ^(NSString* newLabelText, id delegate){
    filterButtonLabel.text = newLabelText;   // command A   
    dispatch_after(DISPATCH_SECONDS_FROM_NOW(1), dispatch_get_main_queue(), ^{
        [delegate abortFilter];  //command B
    });
};

I also tried changing the queue from dispatch_get_main_queue() to dispatch_get_global_queue() and vice versa.. still no luck..

ideas?


update (with correct answer)

based on Mohannad's answer below.. this is what my final solution looks like (note: it turns out that both command A and command B involves UI changes.. so both should be done on the main queue.. the interesting part is that the the whole block was placed on a global queue.. which gave increased the delay in their execution, I also took out the delegate as it wasn't necessary)

void (^labelUpdater)(NSString *, id) = ^(NSString* newLabelText){
    dispatch_async(dispatch_get_main_queue(), ^ {
        filterButtonLabel.text = newLabelText;   // command A -> includes a UI change
    });
    
    dispatch_after(DISPATCH_SECONDS_FROM_NOW(2), dispatch_get_main_queue(), ^{
        [self abortFilter];  //command B -> also includes a UI change
    });
};
Community
  • 1
  • 1
abbood
  • 23,101
  • 16
  • 132
  • 246
  • Take a look at this answer, it's a good solution: http://stackoverflow.com/a/14830103/1228534 – graver May 22 '13 at 10:28
  • I think you should move the command A in the same block with command A and apply the delay in your `abortFilter` command, in this way both commands are executed on the UI thread (as they should be). – danypata May 22 '13 at 12:03
  • yeah delegate wasn't needed.. took it out in my final answer above – abbood May 22 '13 at 20:15

1 Answers1

2

UIKit is not thread safe. taskQ is not the main queue, so command A will execute on a global queue not the main queue.

Consider assigning the main queue to taskQ, or making command A execute on distpatch_async on the main queue.

Mohannad A. Hassan
  • 1,650
  • 1
  • 13
  • 24
  • dispatch_get_main_queue(), returns the UI queue which is associated with the UI thread, so I think you shouldn't change the `main queue` :)) – danypata May 22 '13 at 12:00
  • This isn't what I meant. I suggested assigning the `main queue` to `taskQ` instead of assigning a global queue. I'll edit my answer to be more clear. – Mohannad A. Hassan May 22 '13 at 13:36
  • not that i'm ignoring your response.. but i've tried it and got some other side effects.. lemme do some more digging and i'll update the question accordingly – abbood May 22 '13 at 17:42
  • yup worked out.. i'll award you the correct answer, but i'll also updater my question to show how i followed your advice – abbood May 22 '13 at 19:59
  • also i'm not sure why you mentioned that UIKit is not thread safe.. how is that relevant? keep in mind that GCD doesn't deal with threads, it rather deals with operation queues. – abbood May 22 '13 at 20:27
  • Sorry for taking long to reply. As I understand, UIKit hold its data and performs most of its job inside the main queue. When you try to access it from another queue, you may access a mutually exclusive section. See [this link](http://tirania.org/monomac/archive/2012/Sep-10.html). – Mohannad A. Hassan May 23 '13 at 16:46