5

私はキューとそれらがどのように機能するかをよりよく理解しようとしています。このスニペットは、彼らの行動をテストするためのものです:

- (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" );
}

予想されるように、ログにこれが生成されます。

Begin test on main thread
Waiting for worker
Signalling semaphore
All sync'd up

奇妙なことに、このコードが、たとえば、UIViewControllerの-(void)viewDidAppear:(BOOL)animatedから呼び出されると、動作が変化します。具体的には、次のログでデッドロックします。

Begin test on main thread
Waiting for worker

私の質問は、NSRunLoop runModeがこの状況でdispatch_asyncを介して送信されたブロックを処理しないのに、他の状況では処理するのはなぜですか?

4

1 に答える 1

5

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

フレーム#11に注目してください。への呼び出し_dispatch_main_queue_callback_4CFです。これは、メインキューに配置されたブロックを実行する関数です。したがってviewDidAppear:、実際には、でメインキューに追加されたブロック内から呼び出されましたdispatch_async

メインキューはシリアルキューです。シリアルキューの定義は、一度に1つのブロックのみを実行するキューです。キューがブロックを実行している場合、そのキューの他のブロックは開始できません。したがって、メイン実行ループを再帰的に実行すると、実行ループは、メインキューで実行されているブロック内にすでに存在していることを確認し、それ以上のブロックを開始しようとはしません。そのため、セマフォシグナリングブロックが実行されず、アプリがハングします。

viewDidAppear:キューに入れられたブロック内から呼び出されることもあれば、呼び出されないこともあることに注意してください。どちらの振る舞いにも頼るべきではありません。

于 2012-12-01T07:27:25.613 に答える