NSRunLoop runMode并不总是处理dispatch_async

mac*_*lla 5 objective-c grand-central-dispatch nsrunloop dispatch-async

我试图更好地了解队列及其工作原理。这个片段是为了测试他们的行为:

- (void)dispatchQueueTest
{
    NSLog( @"Begin test on %@ thread", [NSThread isMainThread] ? @"main" : @"other" );
    dispatch_semaphore_t s = dispatch_semaphore_create(0);

    dispatch_async( dispatch_get_main_queue(), ^{
        NSLog( @"Signalling semaphore" );
        dispatch_semaphore_signal(s);
    });

    NSLog( @"Waiting for worker" );
    while( dispatch_semaphore_wait( s, DISPATCH_TIME_NOW ) ) {
        NSDate* timeout = [NSDate dateWithTimeIntervalSinceNow:10.f];
        // Process events on the run loop
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout];
    }
    dispatch_release(s);
    NSLog( @"All sync'd up" );
}
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,它会在日志中生成以下内容:

Begin test on main thread
Waiting for worker
Signalling semaphore
All sync'd up
Run Code Online (Sandbox Code Playgroud)

奇怪的是,如果这段代码是从 UIViewController 的 - (void)viewDidAppear:(BOOL)animated 调用的,那么它会改变行为。具体来说,它与以下日志发生死锁:

Begin test on main thread
Waiting for worker
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么 NSRunLoop runMode 在这种情况下不处理通过dispatch_async发送的块,但在其他情况下却处理?

rob*_*off 5

我有一个项目,我将 a 推PlayerNameEntryViewController到导航控制器上。我在 中放置了一个断点-[PlayerNameEntryViewController viewDidAppear:]。这是命中断点时的堆栈跟踪:

#0  0x0002d3d3 in -[PlayerNameEntryViewController viewDidAppear:] at /Volumes/b/Users/mayoff/t/hotseat2/hotseat2/Home/PlayerNameEntryViewController.m:39
#1  0x00638fbf in -[UIViewController _setViewAppearState:isAnimating:] ()
#2  0x006392d4 in -[UIViewController __viewDidAppear:] ()
#3  0x006395d7 in -[UIViewController _endAppearanceTransition:] ()
#4  0x00648666 in -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] ()
#5  0x007ee90e in -[UINavigationTransitionView _notifyDelegateTransitionDidStopWithContext:] ()
#6  0x007eec17 in -[UINavigationTransitionView _cleanupTransition] ()
#7  0x007eec86 in -[UINavigationTransitionView _navigationTransitionDidStop] ()
#8  0x005a2499 in -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] ()
#9  0x005a2584 in -[UIViewAnimationState animationDidStop:finished:] ()
#10 0x00497e00 in CA::Layer::run_animation_callbacks(void*) ()
#11 0x02e86515 in _dispatch_main_queue_callback_4CF ()
#12 0x015fe833 in __CFRunLoopRun ()
#13 0x015fddb4 in CFRunLoopRunSpecific ()
#14 0x015fdccb in CFRunLoopRunInMode ()
#15 0x01acd879 in GSEventRunModal ()
#16 0x01acd93e in GSEventRun ()
#17 0x00571a9b in UIApplicationMain ()
#18 0x00002461 in main at /Volumes/b/Users/mayoff/t/hotseat2/hotseat2/main.m:17
Run Code Online (Sandbox Code Playgroud)

注意#11 帧。这是一个电话_dispatch_main_queue_callback_4CF。这是运行放在主队列上的块的函数。SoviewDidAppear:实际上是从添加到主队列的块内部调用的dispatch_async

主队列是串行队列。串行队列的定义是一种一次只执行一个块的队列。如果队列正在执行一个块,则该队列上的其他块无法启动。因此,当您递归运行主运行循环时,运行循环会发现它已经在主队列上运行的块内,并且不会尝试启动更多块。这就是为什么您的信号量信号块永远不会运行并且您的应用程序挂起的原因。

请注意,有时viewDidAppear:是从排队块内部调用的,有时则不是。您不应该依赖这两种行为。