I need to dispatch a block on the main queue, synchronously. I don’t know if I’m currently running on the main thread or no. The naive solution looks like this:
For syncing on the main queue or on the main thread (that is not the same) I use:
import Foundation
private let mainQueueKey = UnsafeMutablePointer<Void>.alloc(1)
private let mainQueueValue = UnsafeMutablePointer<Void>.alloc(1)
public func dispatch_sync_on_main_queue(block: () -> Void)
{
struct dispatchonce { static var token : dispatch_once_t = 0 }
dispatch_once(&dispatchonce.token,
{
dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil)
})
if dispatch_get_specific(mainQueueKey) == mainQueueValue
{
block()
}
else
{
dispatch_sync(dispatch_get_main_queue(),block)
}
}
extension NSThread
{
public class func runBlockOnMainThread(block: () -> Void )
{
if NSThread.isMainThread()
{
block()
}
else
{
dispatch_sync(dispatch_get_main_queue(),block)
}
}
public class func runBlockOnMainQueue(block: () -> Void)
{
dispatch_sync_on_main_queue(block)
}
}
I recently began experiencing a deadlock during UI updates. That lead me this Stack Overflow question, which lead to me implementing a runOnMainQueueWithoutDeadlocking
-type helper function based on the accepted answer.
The real issue, though, is that when updating the UI from a block I had mistakenly used dispatch_sync
rather than dispatch_async
to get the Main queue for UI updates. Easy to do with code completion, and perhaps hard to notice after the fact.
So, for others reading this question: if synchronous execution is not required, simply using dispatch_**a**sync
will avoid the deadlock you may be intermittently hitting.
I need to use something like this fairly regularly within my Mac and iOS applications, so I use the following helper function (originally described in this answer):
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
which you call via
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
This is pretty much the process you describe above, and I've talked to several other developers who have independently crafted something like this for themselves.
I used [NSThread isMainThread]
instead of checking dispatch_get_current_queue()
, because the caveats section for that function once warned against using this for identity testing and the call was deprecated in iOS 6.