I want to execute some code asynchronously and have for this reason started using GCD for OSX/iOS.
Currently I am using the function dispatch_async().
When I want to execute something concurrently on another thread, I use the function dispatch_get_global_queue().
When I want to dispatch the result to the main thread, I use the function dispatch_get_main_queue().
But the results never arrive to the main thread.
When setting up breakpoints in the debugger (at the dispatch_async lines), the block after function dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
gets executed only sometimes, but often the debugger just ignores it.
When the block does get executed and execution flow gets to the breakpoint at dispatch_async(dispatch_get_main_queue(), the block after that always gets ignored.
Without dispatch_async(dispatch_get_global_queue) and dispatch_async(dispatch_get_main_queue) the code executes as it should, albeit synchronously.
My question is why does dispatch_async(dispatch_get_main_queue() inside of the dispatch_async(dispatch_get_global_queue() block never get executed?
And on the same note, why is the dispatch_async(dispatch_get_global_queue() block not getting executed every time?
My development environment is
OS: OS X 10.11.3
IDE: Xcode 7.2
Compiler: Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.3.0
Here is a simple example which reproduces the erratic behavior (an OS X console application):
TestClass.h
#ifndef TestClass_h
#define TestClass_h
@interface TestClass : NSObject {
}
- (void)testMethod:(NSString *)testString withCompletionBlock:(void(^)(NSString *blockResult, NSError __autoreleasing *error))completionBlock;
@end
#endif
TestClass.m
#import <Foundation/Foundation.h>
#import "TestClass.h"
@implementation TestClass
- (void)testMethod:(NSString *)testString withCompletionBlock:(void(^)(NSString *blockResult, NSError __autoreleasing *error))completionBlock {
__block NSString *stringResult = nil;
if (completionBlock) {
__block NSError *resultError = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// THIS BLOCK IS CALLED ONLY SOMETIMES, MOST OF THE TIME IT IS IGNORED.
if ([testString isEqual: @"Error string"]) {
NSDictionary *errorUserInfo = @{ NSLocalizedDescriptionKey: @"This is an error.", NSLocalizedFailureReasonErrorKey: @"", NSLocalizedRecoverySuggestionErrorKey: @"" };
resultError = [[NSError alloc] initWithDomain:@"com.test.TestErrorDomain" code:10 userInfo:errorUserInfo];
}
else {
stringResult = testString;
}
dispatch_async(dispatch_get_main_queue(), ^{
// THIS BLOCK NEVER GETS EXECUTED.
completionBlock(stringResult, resultError);
});
});
}
}
@end
main.m
#import <Foundation/Foundation.h>
#import "TestClass.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block NSString *resultString = nil;
TestClass * testObject = [[TestClass alloc] init];
// Output for this call should be: The result string is: Test string.
[testObject testMethod:@"Test string" withCompletionBlock:^(NSString *blockString, NSError __autoreleasing *error) {
resultString = blockString;
if (resultString) {
NSLog(@"The result string is: %@.", resultString);
}
}];
// Output for this call should be: Error: This is an error.
[testObject testMethod:@"Error string" withCompletionBlock:^(NSString *blockString, NSError __autoreleasing *error) {
resultString = blockString;
if (!resultString) {
if (error) {
NSLog(@"Error: %@", [error localizedDescription]);
}
else {
NSLog(@"Error not recognized!");
}
}
}];
}
return 0;
}