I am doing some experiment based on this answer: https://stackoverflow.com/a/17921058/767653
This is my minimum reproducible code:
- (void)viewDidLoad {
[super viewDidLoad];
__block BOOL completed = NO;
[self doMyWork:^{
completed = YES;
}];
while (!completed) {
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
}
My understanding is that, this code has a deadlock. Because the completion block is run on main thread, so in order for the completion block to run, viewDidLoad
must return. And in order for viewDidLoad
to return, completion block must be run to toggle the completed
flag (hence circular wait).
However, when I run it, there's no deadlock in this code. Where I did understand wrongly?
You said:
My understanding is that, this code has a deadlock. Because the completion block is run on main thread, so in order for the completion block to run,
viewDidLoad
must return. And in order forviewDidLoad
to return, completion block must be run to toggle the completed flag (hence circular wait).
Yes and no.
Technically, it is not a deadlock, as the run loop is still processing events. Your UI may appear to be responsive. But because viewDidLoad
is not returning, some other events (like viewDidAppear
) will not be called. There will be other consequences, too. This code interferes with the normal sequence of UI events, but is not technically a “deadlock”.
Bottom line, you must allow viewDidLoad
(and all UIKit events) to promptly return. So, either dispatch this asynchronously (but avoid using the same serial queue for both the dispatch_async
and the dispatch_after
), or better, lose this spinning on the run loop, altogether.
Nowadays, spinning on the main run loop is almost always a mistake. GCD offers nice contemporary solutions. We would need more details regarding why you are spinning at all, though, to answer this broader question.