首页 文章

为什么dispatch_async之后的块(dispatch_get_main_queue()永远不会被调用?

提问于
浏览
1

我想异步执行一些代码,因此开始使用GCD for 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_global_queue()块中的dispatch_get_main_queue()永远不会被执行?
同样,为什么dispatch_async(dispatch_get_global_queue()块每次都没有被执行?

我的开发环境是
操作系统: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;
}

2 回答

  • 1

    这是因为一旦main()函数退出,就会释放TestClass对象 . 主要是异步,testMethod:不会阻塞main()中的指针 .

    尝试在main()的末尾添加一个信号量 . 应在testMethod dispatch_async(dispatch_get_main_queue(), ^{}); 块中发信号通知此信号量 .

  • 3

    这是带有信号量的工作示例,正如@ 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;
    }
    

相关问题