为什么 dispatch_async(dispatch_get_main_queue() 之后的块永远不会被调用?
Why does the block after dispatch_async(dispatch_get_main_queue() never get called?
我想异步执行一些代码,因此开始使用 GCD OSX/iOS。
目前我正在使用函数 dispatch_async().
当我想在另一个线程上同时执行某事时,我使用函数 dispatch_get_global_queue().
当我想将结果分派给主线程时,我使用函数 dispatch_get_main_queue().
但是结果永远不会到达主线程。
在调试器中设置断点时(在 dispatch_async 行),函数 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
只是有时会执行,但调试器通常会忽略它。
当块确实被执行并且执行流程到达 dispatch_async(dispatch_get_main_queue() 处的断点时,之后的块总是被忽略。
没有dispatch_async(dispatch_get_global_queue)和dispatch_async(dispatch_get_main_queue) 代码按预期执行,尽管是同步执行的。
我的问题是为什么 dispatch_async(dispatch_get_main_queue() 在 dispatch_async(dispatch_get_global_queue() 块永远不会被执行?
同样,为什么 dispatch_async(dispatch_get_global_queue() 块不是每次都执行?
我的开发环境是
OS: OS X 10.11.3
IDE: Xcode 7.2
编译器:Apple LLVM 版本 7.0.2 (clang-700.1.81)
目标:x86_64-apple-darwin15.3.0
这是一个重现不稳定行为的简单示例(OS X 控制台应用程序):
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;
}
那是因为一旦 main() 函数退出,您的 TestClass 对象就会被释放。主要是异步的,testMethod: 不会阻塞 main() 中的指针。
尝试在 main() 的末尾添加一个信号量。这个信号量应该在 testMethod dispatch_async(dispatch_get_main_queue(), ^{});
块中发出信号。
这是带有信号量的工作示例,正如@CharlesThierry 的回答中所建议的那样。
我还包含了一些有助于说明执行流程的 NSLog:
TestClass.h
#ifndef TestClass_h
#define TestClass_h
@interface TestClass : NSObject {
}
- (void)testMethod:(NSString *)testString withSemaphore:(dispatch_semaphore_t)sem 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 withSemaphore:(dispatch_semaphore_t)sem withCompletionBlock:(void(^)(NSString *blockResult, NSError __autoreleasing *error))completionBlock {
__block NSString *stringResult = nil;
if (completionBlock) {
__block NSError *resultError = nil;
NSLog(@"INSIDE TEST METHOD, OUTSIDE DISPATCH ASYNC");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"INSIDE THE DISPATCH ASYNC TO THE GLOBAL QUEUE");
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(), ^{
NSLog(@"INSIDE THE DISPATCH ASYNC TO THE MAIN QUEUE!");
completionBlock(stringResult, resultError);
dispatch_semaphore_signal(sem);
});
});
}
}
@end
main.m
#import <Foundation/Foundation.h>
#import "TestClass.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"BEFORE BLOCK");
__block NSString *resultString = nil;
TestClass * testObject = [[TestClass alloc] init];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[testObject testMethod:@"Test string" withSemaphore:sem withCompletionBlock:^(NSString *blockString, NSError __autoreleasing *error)
{
NSLog(@"INSIDE FIRST BLOCK");
resultString = blockString;
if (resultString)
{
NSLog(@"The result string is: %@.", resultString);
}
}];
[testObject testMethod:@"Error string" withSemaphore:sem withCompletionBlock:^(NSString *blockString, NSError __autoreleasing *error)
{
NSLog(@"INSIDE SECOND BLOCK");
resultString = blockString;
if (!resultString)
{
if (error)
{
NSLog(@"Error: %@", [error localizedDescription]);
}
else
{
NSLog(@"Error not recognized!");
}
}
}];
NSLog(@"AFTER BLOCK!");
while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW))
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
return 0;
}
我想异步执行一些代码,因此开始使用 GCD OSX/iOS。
目前我正在使用函数 dispatch_async().
当我想在另一个线程上同时执行某事时,我使用函数 dispatch_get_global_queue().
当我想将结果分派给主线程时,我使用函数 dispatch_get_main_queue().
但是结果永远不会到达主线程。
在调试器中设置断点时(在 dispatch_async 行),函数 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
只是有时会执行,但调试器通常会忽略它。
当块确实被执行并且执行流程到达 dispatch_async(dispatch_get_main_queue() 处的断点时,之后的块总是被忽略。
没有dispatch_async(dispatch_get_global_queue)和dispatch_async(dispatch_get_main_queue) 代码按预期执行,尽管是同步执行的。
我的问题是为什么 dispatch_async(dispatch_get_main_queue() 在 dispatch_async(dispatch_get_global_queue() 块永远不会被执行?
同样,为什么 dispatch_async(dispatch_get_global_queue() 块不是每次都执行?
我的开发环境是
OS: OS X 10.11.3
IDE: Xcode 7.2
编译器:Apple LLVM 版本 7.0.2 (clang-700.1.81)
目标:x86_64-apple-darwin15.3.0
这是一个重现不稳定行为的简单示例(OS X 控制台应用程序):
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;
}
那是因为一旦 main() 函数退出,您的 TestClass 对象就会被释放。主要是异步的,testMethod: 不会阻塞 main() 中的指针。
尝试在 main() 的末尾添加一个信号量。这个信号量应该在 testMethod dispatch_async(dispatch_get_main_queue(), ^{});
块中发出信号。
这是带有信号量的工作示例,正如@CharlesThierry 的回答中所建议的那样。 我还包含了一些有助于说明执行流程的 NSLog:
TestClass.h
#ifndef TestClass_h
#define TestClass_h
@interface TestClass : NSObject {
}
- (void)testMethod:(NSString *)testString withSemaphore:(dispatch_semaphore_t)sem 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 withSemaphore:(dispatch_semaphore_t)sem withCompletionBlock:(void(^)(NSString *blockResult, NSError __autoreleasing *error))completionBlock {
__block NSString *stringResult = nil;
if (completionBlock) {
__block NSError *resultError = nil;
NSLog(@"INSIDE TEST METHOD, OUTSIDE DISPATCH ASYNC");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"INSIDE THE DISPATCH ASYNC TO THE GLOBAL QUEUE");
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(), ^{
NSLog(@"INSIDE THE DISPATCH ASYNC TO THE MAIN QUEUE!");
completionBlock(stringResult, resultError);
dispatch_semaphore_signal(sem);
});
});
}
}
@end
main.m
#import <Foundation/Foundation.h>
#import "TestClass.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"BEFORE BLOCK");
__block NSString *resultString = nil;
TestClass * testObject = [[TestClass alloc] init];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[testObject testMethod:@"Test string" withSemaphore:sem withCompletionBlock:^(NSString *blockString, NSError __autoreleasing *error)
{
NSLog(@"INSIDE FIRST BLOCK");
resultString = blockString;
if (resultString)
{
NSLog(@"The result string is: %@.", resultString);
}
}];
[testObject testMethod:@"Error string" withSemaphore:sem withCompletionBlock:^(NSString *blockString, NSError __autoreleasing *error)
{
NSLog(@"INSIDE SECOND BLOCK");
resultString = blockString;
if (!resultString)
{
if (error)
{
NSLog(@"Error: %@", [error localizedDescription]);
}
else
{
NSLog(@"Error not recognized!");
}
}
}];
NSLog(@"AFTER BLOCK!");
while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW))
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
return 0;
}