我在使用内置的Xcode单元测试框架SenTestingKit测试一些宏的中央调度代码时遇到了一些麻烦。我成功地解决了我的问题。我有一个单元测试,它构建一个块并尝试在主线程上执行它。但是,该块从未实际执行过,因此测试会挂起,因为它是同步调度。
- (void)testSample {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
NSLog(@"on main thread!");
});
STFail(@"FAIL!");
}
导致它挂起的测试环境是什么?
答案 0 :(得分:104)
dispatch_sync
在给定队列上运行一个块并等待它完成。在这种情况下,队列是主调度队列。主队列以FIFO(先进先出)顺序在主线程上运行其所有操作。这意味着无论何时调用dispatch_sync
,您的新块都将放在该行的末尾,并且在队列中的所有其他内容完成之前不会运行。
这里的问题是你刚入列的块是在等待在主线程上运行的行的testSample
方法 目前正在主线程上运行。在当前方法(本身)使用主线程完成之前,队列末尾的块无法访问主线程。但是dispatch_sync
表示提交一个块对象以便在调度队列上执行,并等待该块完成。
答案 1 :(得分:8)
代码中的问题是,无论您使用dispatch_sync
还是dispatch_async
,都会始终调用STFail()
,导致您的测试失败。
更重要的是,正如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!");
}
答案 2 :(得分:6)
如果您在主队列中并同步等待主队列可用,您确实会等待很长时间。你应该测试以确保你还没有在主线程上。
答案 3 :(得分: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!");
}
答案 4 :(得分:2)
如果你必须等待让自己先出屋,你会不会离开家?你猜对了!没有! :
基本上如果:
main_queue
)sync
调用该方法,即以串行方式调用该方法,并希望在FooQueue上执行。 永远不会出于同样的原因,你永远不会离开家!
它不会被派遣,因为它必须等待自己离开队列!