I can't address your first point because I haven't used GC yet, but your second seems like a contradiction. If you're releasing from a background thread then you are sending messages from a background thread. You can easily put code in our object's release method to fix that, though. "If not main thread, call self on main thread."
This is a hack, not a general purpose solution, as it requires modifying the class, and and still must be done carefully to avoid accidental retain/release.
Calling retain/release from a background thread will happen simply by using blocks, gcd, or performSelector: APIs, as designed, to attempt to schedule execution on the main thread. Avoiding the issue requires exquisite care, unless you actually want to modify every single class that you may access an instance of from a background thread (such as GCD).
As to the first point, it applies in a NON-GC environment, where you hold weak references to delegates a(nd deregister them in dealloc.)
Sorry, the term "weak reference" threw me. So you mean you have pointers to objects you have not retained, and you "deregister" (set them to nil?) in your dealloc? I don't see how that can trigger a race condition or call anyone else's dealloc.
Think delegates, or observers. There is a race condition between calling the delegate and deregistration of the delegate. Once an object's dealloc method has been called, you must not resurrect it, but there is a race between the execution of -dealloc, the de-registration of the delegate, and the dispatch of messages to the delegate.
So you're setting threadedObject.delegate = nil in your delegate object's dealloc, but the threadedObject is sending subsequent messages to your delegate? The doesn't sounds like a retain-release problem. Wouldn't the same thing happen when setting threadedObject.delegate=foo? Messages could still get send to the old delegate if you haven't done your synchronization right.
Missing the forest for the trees. The problem is that the old delegate will be invalidated before it deregisters itself, because it will not deregister itself until it is invalid (during -dealloc)