It all started with this snippet from @jspahrsummers
#import <Foundation/Foundation.h>
int main (int argc, const char **argv) {
@autoreleasepool {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"is main thread? %i", (int)[NSThread isMainThread]);
});
});
dispatch_main();
}
return 0;
}
2015-11-24 16:08:39.545 main[88889:21446128] is main thread? 0
with this comment:
[NSThread isMainThread] is probably not what you want!
and this is true. In general. With exceptions.
blocks submitted to the main queue in applications using
dispatch_main()
are not guaranteed to execute on the main thread.
The thing is that this applies to applications that use dispatch_main()
like the one from the snippet. However for Cocoa applications, this is not a case because
Cocoa applications need not call dispatch_main().
Let's search how it is documented for Cocoa application:
dispatch_get_main_queue()
function returns the queue associated with application’s main thread
This queue is created automatically for Cocoa applications and for applications that either call the
dispatch_main()
function or configure a run loop (using either theCFRunLoopRef
type or anNSRunLoop
object) on the main thread.[1]
This queue is created automatically on behalf of the main thread before main is called.
...and main run loop
The run loop for your application’s main thread is a crucial piece of infrastructure. As a result, the app frameworks provide the code for running the main application loop and start that loop automatically. [2]
and
Blocks submitted to the main queue will be executed as part of the "common modes" of the application's main NSRunLoop or CFRunLoop.
Ok, so for Cocoa application we have:
- Main thread
- Main queue associated with Main thread
- Main loop associated with Main thread
It's all should result in block execution in Main thread, shouldn't it? It is simply shown that snippet from the beginning will always return 1
when used with UIApplicationMain()
or NSApplicationMain()
instead of dispatch_main()
.
Check if main queue
When checking source code for libdispatch, this is quite a common comment to the functions:
Blocks submitted to the main queue MUST be run on the main thread.
and this is mostly compatibility mode with Cocoa (DISPATCH_COCOA_COMPAT).
Why is it failing sometimes?
There is a problem discussed in ReactiveCocoa issue that checking for main thread is not enough to assume that this is main queue:
Apple DTS “explicitly stated that main queue and the main thread are not the same thing, have subtle differences”. https://t.co/YxAbqkvtse
— Ole Begemann (@olebegemann) June 3, 2016
The question is WHY it is so?
However, don’t be fooled into thinking that queues, work units, and threads are tightly connected.[3]
Threads are reused. Queues reuse threads in general. If so, the Main thread can be reused by another queue (for example by concurrent queue) when Main thread is idle.
Conclusion
- Main Queue IS bound to Main Thread.
- Main Thread IS NOT bound to Main Queue.
That's why in general: Main Queue Thread != Main Thread
Good read about queues is this manual for dispatch_get_current_queue(), highly recomended.
comments?