Please consider this simple example:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"BLOCK!!!");
});
while (YES)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
NSLog(@"RUN LOOP");
}
});
}
The block passed into the second call (3 seconds) to dispatch_after is not fired. However if I don't use the first dispatch_after (2 seconds) then it works as expected. Why?
I know that if I remove the while loop with NSRunLoop running inside then it is working but I need the loop there
You have code which
dispatch_afterto run on the main queue; but thenwhileloop that is repeatedly calling theNSRunLoop.This is just blocking the main thread from doing anything that isn’t invoked directly from the main
NSRunLoop.There are three solutions to this problem:
You can fix this by dispatching the code with the
whileloop to a global (i.e. background) queue:This technique is is a permutation of something we used to do in the pre-GCD days. This is rendered largely useless nowadays. It’s just too inefficient.
You can use a
NSTimerwhich is scheduled and run from theNSRunLoop, so, while you’re still blocking the main queue, at least the timer will fire.This not really a solution to the problem (you’re still blocking the main thread from anything not running from the
NSRunLoopitself), but is illuminating about the nature of the runloop.Or, obviously, it’s best to just remove the
whileloop:Bottom line, nowadays, you practically never spin on a thread (or its runloop). It’s terribly inefficient and GCD offers far more elegant ways to achieve the desired effect.