45

我在使用内置的 Xcode 单元测试框架 SenTestingKit 对一些大型中央调度代码进行单元测试时遇到了一些麻烦。我设法解决了我的问题。我有一个构建块并尝试在主线程上执行它的单元测试。但是,该块从未真正执行过,因此测试挂起,因为它是同步调度。

- (void)testSample {

    dispatch_sync(dispatch_get_main_queue(), ^(void) {
        NSLog(@"on main thread!");
    });

    STFail(@"FAIL!");
}

导致它挂起的测试环境是什么?

4

5 回答 5

113

dispatch_sync在给定队列上运行一个块并等待它完成。在这种情况下,队列是主调度队列。主队列以 FIFO(先进先出)的顺序在主线程上运行其所有操作。这意味着每当您调用 时dispatch_sync,您的新块将被放在行尾,并且在队列中的其他所有内容完成之前不会运行。

这里的问题是,您刚刚入队的块位于等待在主线程上运行的行——而该testSample方法当前正在主线程上运行。在当前方法(本身)完成使用主线程之前,队列末尾的块无法访问主线程。然而dispatch_sync,意味着提交一个块对象以在调度队列上执行并等待该块完成。

于 2011-10-19T06:03:07.323 回答
8

您的代码中的问题是,无论您是否使用dispatch_syncor dispatch_asyncSTFail()都将始终被调用,从而导致您的测试失败。

更重要的是,正如 BJ Homer 所解释的,如果您需要在主队列中同步运行某些东西,您必须确保您不在主队列中,否则会发生死锁。如果您在主队列中,您可以简单地将块作为常规功能运行。

希望这可以帮助:

- (void)testSample {

    __block BOOL didRunBlock = NO;
    void (^yourBlock)(void) = ^(void) {
        NSLog(@"on main queue!");
        // Probably you want to do more checks here...
        didRunBlock = YES;
    };

    // 2012/12/05 Note: dispatch_get_current_queue() function has been
    // deprecated starting in iOS6 and OSX10.8. Docs clearly state they
    // should be used only for debugging/testing. Luckily this is our case :)
    dispatch_queue_t currentQueue = dispatch_get_current_queue();
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    if (currentQueue == mainQueue) {
        blockInTheMainThread();
    } else {
        dispatch_sync(mainQueue, yourBlock); 
    }

    STAssertEquals(YES, didRunBlock, @"FAIL!");
}
于 2011-10-19T03:24:55.247 回答
6

如果您在主队列上并同步等待主队列可用,您确实会等待很长时间。您应该进行测试以确保您尚未在主线程上。

于 2011-10-19T03:33:57.673 回答
4

如果您必须等待自己先出门,您会出门吗?你猜对了!不!:]

基本上如果:

  1. 在 FooQueue 上。(不一定是main_queue
  2. 您使用 ie 以串行方式调用该方法sync并希望在 FooQueue 上执行

出于同样的原因,它永远不会发生,你永远不会离开家!

它永远不会被分派,因为它必须等待自己离开队列!

于 2016-05-24T21:45:45.347 回答
2

跟进,因为

dispatch_get_current_queue()

现在已弃用,您可以使用

[NSThread isMainThread]

看看你是否在主线程上。

因此,使用上面的其他答案,您可以执行以下操作:

- (void)testSample 
{
    BOOL __block didRunBlock = NO;
    void (^yourBlock)(void) = ^(void) {
        NSLog(@"on main queue!");
        didRunBlock = YES;
    };

    if ([NSThread isMainThread])
        yourBlock();
    else
        dispatch_sync(dispatch_get_main_queue(), yourBlock);

    STAssertEquals(YES, didRunBlock, @"FAIL!");
}
于 2015-04-24T15:01:23.450 回答