8
votes

I am having a gui/threading related problem in developing a cocoa user interface. The application is designed like this:

Main Thread (#1): parses arguments, loads plugins, etc.

Gui thread (#?): launches the gui, handles events, etc. Its the gui thread.

The Cocoa framework is non-thread safe, but enforces one rule, the GUI must run on the main thread. A assertion is used to check this. To try to go around this I implemented the run method myself (code below) following this - http://cocoawithlove.com/2009/01/demystifying-nsapplication-by.html - guide. But I am missing something. A window is opened, but stays blank (completely white). Although if I make the call in the main thread it works perfectly.

So basically I need to figure out what's missing.

- (void)run
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [self finishLaunching];

    shouldKeepRunning = YES;
    do
    {
        [pool release];
        pool = [[NSAutoreleasePool alloc] init];

        NSEvent *event =
            [self
                nextEventMatchingMask:NSAnyEventMask
                untilDate:[NSDate distantFuture]
                inMode:NSDefaultRunLoopMode
                dequeue:YES];

        [self sendEvent:event];
        [self updateWindows];
    } while (shouldKeepRunning);

    [pool release];
}

- (void)terminate:(id)sender
{
    shouldKeepRunning = NO;
}
3
Looks like you should just reverse the stuff you're using your threads for. Why fight the framework?jscs
Because I would rather not fight the program's plugin architecture... But I think I will have to concede on this one...OCarlos

3 Answers

13
votes

Don't. This approach will never work. Even if you fix your current problem (the window not drawing) you'll immediately run into another obscure, impossible-to-fix problem, and another, and another. Cocoa expects the GUI thread to be the main thread, end of story.

2
votes

Do all in the background thread except updating the GUI. I see that you have only a line where you need to update the GUI. So do it the way you're doing it, except that you execute all GUI updates in the main thread:

dispatch_async(dispatch_get_main_queue(), ^
{
    [self updateWindows];
});

Now I don't know what's updateWindows, I assumed that this wouldn't create a race condition.

0
votes

Why not reverse the problem? Have the main thread spawn a thread (let's call this the app thread), then block before spawning the GUI. The app thread will parse arguments, load plugins, etc. After it's initialization is done, the app thread will signal the main thread to go ahead and launch the GUI.