In my document-based application, I have overridden the method openDocument: in my subclass of NSDocumentcontroller so that I can display my own openPanel. I pass the chosen URLs to the method openDocumentWithContentsOfURL:display:completionHandler:. I use this code for the call:
[self openDocumentWithContentsOfURL:[chosenFiles objectAtIndex:i] display:YES completionHandler:^(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error) {
if (document == nil)
{
NSAlert* alert = [NSAlert alertWithError:error];
[alert runModal];
}
}];
So I want to display the passed error if nil gets returned as a reference to the document. The problem is, that the program just "freezes" after I press the "Open" button in the open panel. Then I need to manually stop the program with the "stop" button in Xcode. No spinning beach ball appears, though. If I comment the line "[alert runModal]", the program does not freeze any more, but no alert is displayed, of course.
Now the strange thing: The code works sometimes. If I switch from Xcode to my browser and back and I run the program again, it sometimes works flawlessly and the error is displayed. After some time it stops working again. It is unpredictable, but most of the time it doesn't work.
All this sounds like a race-condition to me. It certainly has something to do with the block? But what do I do wrong?
Converting my comment to an answer:
runModel on the main thread.
[alert performSelectorOnMainThread:@selector(runModal) withObject:nil waitUntilDone:NO];
I think runModel needs to be called on the main thread because it's part of the AppKit framework, and it's estentially triggering UI graphics. I believe all calls to the AppKit framework or to any method that manipulates graphics needs to be on the main thread.