Well this was entirely obvious once I sat down and tried to remember the tiny bits of Objective-C I used to know.
When you create threads in PyObjc you have to be careful to also create an instance of NSAutoreleasePool in your thread's run() loop or else you'll get an irritating memory leak.
Exact rules on when/why/how to do this from the Foundation class docs:
If you are making Cocoa calls outside of the Application KitÂ’s main thread, however, you need to create your own autorelease pool. This is the case if you are a Foundation-only application or if you detach a thread. If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should periodically destroy and create autorelease pools (like the Application Kit does on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.
Hmm...
Now that I'm writing this down - I recall something about Cocoa being very not-threadsafe. The suggested way of handling updates to Cocoa using threads was something like posting messages into an event queue or something like that.
Argh... anyone care to point me in the right direction here or am I suffering from false memories again? Is Cocoa threadsafe if I'm doing updates to NSTextView instances from multiple threads?
Addendum: Yeah - there are issues. Note to self - read this.