How to call a method a.s.a.p. but at earliest in the next run loop iteration?
Asked Answered
P

4

18

I need a save way to say: "iOS, I want this method to be executed a.s.a.p., but NOT in THIS run loop iteration. At the earliest in the next, but please not in this one. Thank you."

Right now I am always doing it like this:

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

With the assumption that -doSomeOtherThings will always be performed BEFORE -doSomethingInNextRunLoop.

The documentation says:

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.

So basically it can happen that the method gets called immediately as if I had just sent a direct message, causing -doSomethingInNextRunLoop to be executed before -doSomeOtherThings?

How can I make absolutely sure it will be called a.s.a.p. but NEVER ever in this same run loop iteration?

To clarify the wording: With run loop I mean the main thread, and the iteration in which all methods must return until the thread is ready again for new events.

Promontory answered 17/8, 2011 at 16:22 Comment(0)
L
11

If you're worried that Apple may someday special-case a delay of 0, you could always specify a delay of 1e-37 or so. Although the documentation for performSelector:withObject:afterDelay: could easily be read to guarantee that the selector will always be scheduled for the next run loop iteration.

If you're worried that Apple may someday special-case delays less than some arbitrary lower bound, you could always try using NSRunLoop's performSelector:target:argument:order:modes: which the documentation specifically states will schedule execution for the next iteration of the run loop.

Leinster answered 17/8, 2011 at 16:53 Comment(1)
Superb! A category to easify this would make sense.Promontory
F
9

Quite trivial using GCD (Grand Central Dispatch):

dispatch_async (dispatch_get_main_queue (), ^{
    NSLog (@"This stuff runs in the next iteration of the main run loop");
});
Femineity answered 18/3, 2014 at 12:6 Comment(0)
S
1

I think you conclusion from reading the documentation is wrong.

So basically it can happen that the method gets called immediately as if I had just sent a direct message

No. The part of the documentation you quote says that the selector is always queued on the run loop, no matter what. So it will never be executed just as a direct message.

The first sentence with the "not necessarily" might be a bit misleading, but I think the second sentence should really clarify that what you fear is not gong to happen.

Selfeducated answered 17/8, 2011 at 17:47 Comment(1)
Hopefully you're right, because I bet a lot of people depend on it beeing queued for the next iteration. Still, there's some uncertainity.Promontory
G
0

Surely you just do this;

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

Which guarantees the execution order you want.

Genvieve answered 17/8, 2011 at 16:50 Comment(3)
It is not always convenient (or possible) to rearrange the code in that manner in cases more complex than this trivial example used for illustration.Leinster
Not really. There are good reasons for performing things in the NEXT run loop iteration. One of them: Letting the UI refresh inbetween. Improving perceived performance. Among many others. Besides that, Anomie is right.Promontory
For sure, the response from Anomie hits the nail on the head, I'm just pointing out that in some instances, code-rearranging can solve the problem too. A bit like writers' block, sometimes devs get hung up on solving a problem a particular way when in fact there is a lateral plan B. FWIW I'm not aware of ANY instance where the standard performSelector:withObject:afterDelay:0 results in blocking and use the technique frequently for optimising UI code written by others.Genvieve

© 2022 - 2024 — McMap. All rights reserved.