Cocoa vs Swing vs .NET: invoking a method on the main GUI thread

Work progresses on my first Windows app. I continue to be thoroughly impressed with the quality of Visual Studio, the C# language, and the .NET libraries… but at the same time amazed at the shittiness of Windows in general. Considering how solid the dev tools are, why does this OS suck so bad (BTW, I’m still using XP…)? Text rendering and editing on XP is light years behind OS X (even OS X 10.0).

Anyhow, on to the topic at hand… when developing a desktop GUI application with any toolkit, you immediately need at least a basic understanding of multi-threading support. Two concepts are absolutely essential right off the bat:

  1. Spawning new worker threads from the main GUI thread.
  2. Requesting a block of code execute back on the main GUI thread (usually after the worker thread has completed).

Spawning new threads is usually pretty easy to figure out… but the correct technique for getting back on the main GUI thread is a bit tougher to discover when you are clumsily groping around in a new development environment.

My first GUI toolkit was Swing… Java’s excellent langauge-level support for threading and its (not widely-loved) use of anonymous classes clicked with me very quickly.

For some reason, when I moved to Cocoa, I had a really hard time figuring this one out… even though calling back to the main GUI thread is very simple in Cocoa. I guess I wasn’t expecting the method for accessing the main thread of a graphical application to be declared on the base NSObject class. This is typical of Cocoa’s encumbrance of low-level classes with high-level functionality about which they should really know nothing about. For example: +[NSString stringWithContentsOfURL:encoding:error:]. How did the humble Cocoa string class learn HTTP? ;) Despite this dubious design (or because of it?) Cocoa tends to be extremely easy to use.

Still, in general, the object-oriented threading facilities in Java are waaaaaayyyy better than those in Cocoa… especially as of Java 5.

Yesterday, it was time to learn how to call back to the main GUI thread in C#. As impressed as I am with C# so far, I find the approach C# takes for this common task to be the ugliest and most inconvenient to write. However, it is the most convenient to execute from a client code perspective, so maybe that doesn’t matter…

So here we go… everthing Todd knows about calling back to the main GUI thread in any toolkit ever… from easiest to hardest.

First Cocoa:


//... inside class definition ...

- (void)doStuffOnWorkerThread {
    // ... do worker thread stuff, then...
    id arg = ...
    [self performSelectorOnMainThread:@selector(doStuffOnMainGUIThread:)
                           withObject:arg
                        waitUntilDone:NO];
}

- (void)doStuffOnMainGUIThread:(id)arg {
    // update UI, etc...
}

I told you it was simple. Even if you don’t know a thing about Objective-C’s rather unusual syntax, this code just reads. Cocoa’s @selector() scheme for simulating function pointers is simple as well as easy to understand and read.

Now, Java:


//... inside class definition ...

private void doStuffOnWorkerThread() {
    // ... do worker thread stuff, then...
    final Object arg = ...
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            doStuffOnMainGUIThread(arg);
        }
    });
}

private void doStuffOnMainGUIThread(Object arg) {
    // update UI, etc...
}

It would be hard to argue that the Java code reads as well as the Cocoa code… lots of folks don’t care for anonymous classes at all… which are required for this technique — they’re Java’s answer to function pointers. I’ve always found them kinda sexy and sweet (that’s right, I said anonymous classes are sexy and sweet), so I don’t mind, but then again, most people do.

The requirement that the arg variable be final, however, is a real pain in the ass. (BTW, looks like Java 7 may introduce alternatives for this task)

Finally, C#:


//... inside class definition ...

delegate void DoStuffOnMainGuiThreadCallback(object arg);

private void DoStuffOnWorkerThread() {
    // ... do worker thread stuff, then...
    object arg = ...
    DoStuffOnMainGuiThread(arg);
}

private void DoStuffOnMainGuiThread(object arg) {
    if (InvokeRequired) {
        DoStuffOnMainGuiThreadCallback cb = new DoStuffOnMainGuiThreadCallback(DoStuffOnMainGuiThread);
        Invoke(cb, new object[] { arg ]});
    }
        // update UI, etc...
    }
}

Eeewwww. Told ya it was ugly. This technique is kinda nice from a client code perspective… just call the method you want executed on the main thread and the called method does all the thread management. I suppose in that sense, C# has a slight advantage over Java, which places all the work on the client code. However, defining and executing the delegate is just such a bore… and so many lines.

I think Cocoa is the simpliest solution to implement all-around, while Java’s solution strikes me as the most logical and cleanly designed. But then again, the C# technique is the easiest on client code, which is important.

I think that Cocoa wins overall here for simplicity (as usual)… what do you think???


About this entry