Search code examples
iosobjective-c

Is run loop synchronous or asynchronous?


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?


Solution

  • 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 for viewDidLoad 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.