Perform on Next Run Loop: What's Wrong With GCD?
Asked Answered
A

2

31

I'm trying these two approaches:

dispatch_async(dispatch_get_main_queue(),^{
    [self handleClickAsync];
});

and

[self performSelector:@selector(handleClickAsync) withObject:nil afterDelay:0];

in response to a button press.

The second allows the UIButton to highlight as one would expect and perform the handleClickAsync on the next run loop (I suppose: "sometime later" for sure). The first does not allow the UIButton instance to light up until the operation is completely done.

What is the correct way to do this with GCD, or is performSelector still the only way?

Auburn answered 3/5, 2012 at 22:52 Comment(11)
Presumably you're calling this from inside another dispatch queue? If so, it will run all commands sequentially within the queue. Is it possible for you to call this method before you start the operation?Sadler
I'm not sure. I'm calling this directly from a touchesEnded, which comes for free on the UIView subclass (or UIButton in this case).Auburn
I'm not sure why you are making an association between dispatch_async and the running of the runloop...Wreckage
@Wreckage I'm assuming that until the runloop completes, the UIButton cannot finish updating its stuff. That's not correct?Auburn
Yes but I thought GCD just scheduled the task on the queue I don't think it cares about letting the runloop complete. If it need to be done async then why not run on a background queue and call back to the main queue on completion?Wreckage
Okay, I see your point @Paul.s. The operation is not async, but the visual feedback to the user that the button has been pressed-and-released must be immediate. On the other hand, the operation is 99% on the main thread, hence the performSelector:withObject:afterDelay:0 works out nicely. Hmmmm...Auburn
Maybe using a delay of 1 will give the button time to change the view?Rosinski
What does -handleClickAsync do? Does it interact with the UI or any UIKit objects?Karenkarena
@Sean, yes, otherwise I'd fire it off on a background queue and not worry about it :)Auburn
@Yar use touch up inside rather than touches ended for button taps.Sauer
@indiekiduk they're different universes. TouchUpInside is an event.Auburn
K
45

I believe the answer is found here in a discussion of the main dispatch queue:

This queue works with the application’s run loop (if one is present) to interleave the execution of queued tasks with the execution of other event sources attached to the run loop.

In other words, the main dispatch queue sets up a secondary queue (alongside the standard event queue provided by UIApplicationMain() for handling blocks submitted to the main queue. When blocks are present in the queue, the run loop will alternate dequeuing tasks from the main event queue and the dispatch queue. On the other hand, the reference for the delay parameter of -performSelector:withObject:afterDelay: notes that:

Specifying a delay of 0 does not necessarily cause the selector to be performed immediately. The selector is still queued on the thread’s run loop and performed as soon as possible.

Thus, when you use perform selector, the operation is queued at the end of the main event queue, and will not be performed until after everything in front of it in the queue (presumably including the code to unhighlight the UIButton) has been processed. When you use the main dispatch queue, however, it adds the block into the secondary queue which then likely gets processed immediately (i.e., on the next run loop) assuming there are no other blocks in the main queue. In this case, the code to unhighlight the button is still sitting in the main event queue while the run loop processes the event from the secondary block queue.

Karenkarena answered 4/5, 2012 at 14:30 Comment(2)
This makes sense, Sean, and seems to explain the empirical reality. I'm going to wait for a few days to mark this accepted to see what other kinds of answers come in.Auburn
Hmmm... hopefully somebody like @bbum would come in and verify this stuff, but that's just not how SO works, so I'll mark this as best answer provisionally (forever). Thanks again.Auburn
S
28

I think this will hit your point:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
     //bla bla bla
}];
Secern answered 27/12, 2012 at 6:13 Comment(1)
Works a treat 7 years laterDoghouse

© 2022 - 2024 — McMap. All rights reserved.