2016-03-19 9 views
1

次のコードでは、アウターループのRunLoopをチェックし、dispatch_afterを使用して内部ループのmain_threadにディスパッチします。私は、これが呼び出される2つのケースを持っています。ひとつは、ナビゲーションバーのボタンが押され、もう一つがviewDidAppearの間です。後でコードを呼び出すと、RunLoopにスタックされたままになり、ブレークポイントは私のdispatch_afterブロックに決してぶつからない。なぜdispatch_afterがブロックされるのですか?コード実行中でもいくつかの進捗インジケータを更新するには、これを行う必要があります。なぜこれがあるケースではうまく動作しないのは明らかですか?私はスタックを見て、そこに他のスタックはあまりありません。メインキューのdispatch_asyncブロックがモーダル実行ループで実行されない

// case 1: 

// does not hit breakpoint 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.4 * NSEC_PER_SEC),  dispatch_get_main_queue(),^
     { 
      [ self longRunningOp ]; 
     }); 

// case 2: 

// This works fine! 
[ self longRunningOp ]; 



-(void) longRunningOp 
{ 
    bool dispatched = false; 
    while (!finished) 
    { 
     if (dispatched) 
     { 
      // reusing the same date to avoid buffers building up! 
      date = [ date initWithTimeIntervalSinceNow:0 ]; 
      [ [ NSRunLoop currentRunLoop ] runMode: NSDefaultRunLoopModes beforeDate:date ]; 
      continue; 
     } 

     dispatched = true; 

     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC), dispatch_get_main_queue(), ^() { 
      // In second case breakpoint never gets here! 

      // OPENGL OPS HERE! 

      dispatched = false; 
     } 

    }); 
} 

他の相違点にも気付きました。

成功事例:それが動作する場合 、UIApplicationMainはCFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTIONに呼び出しを行い、ユーザーがナビゲーションボタンを押したときに、これが起こります。

障害ケース: それが失敗した場合には、UIApplicationMainはCFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUEに呼び出しながら、これはviewDidAppearで発生します。

+0

'longRunningOp'でループを停止するには、' finished'をtrueに設定しますか? 'longRunningOp'はメインスレッドをブロックします。長時間実行される操作は、バックグラウンド・キューにディスパッチする必要があります。 – Paulw11

+1

あなたの正気を保つために、実行ループを操作しないようにしてください。あなたがstackoverflowでそれについての質問をするなら、あなたは本当に実行ループから離れるべきです。 – gnasher729

+0

@ Paulw11 - それは問題だったと思うが、問題はlongRunningOpにさえ達していないということだ。したがって、ブロックはスケジュールされません。この長い実行中のopは、実際にはOpenGLで処理していて、メインスレッド上にある必要があります。しかし、長期的なオペレーションが終わった後であなたの質問に答えることは、終わったと言います。 – nishant

答えて

0

多くのデバッグと思考の後、私は動作するものがあります。私はそれがなぜ起こるのか説明できませんが、それは私のために解決します。なぜ私が変更を行ったのか説明できます。私は成功事例のためにスタックを慎重に調べ、stack関数の先頭がperformSelector:withObject:afterDelayコールをdispatch_async呼び出しに対して対応させていれば動作することがわかりました。これは私にできることの手がかりを与えました。一番外側のdispatch_asynchをperformSelector:withObject:afterDelayに置き換えました。今はデッドロックなしで動作します。私は下記の失敗事例と成功事例を挙げました。

// This causes the queue to block; -- FAILURE CASE 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.4 * NSEC_PER_SEC),   dispatch_get_main_queue(),^
    { 
     [ self longRunningOp ]; 
    }); 


// This keeps the queue running;!!!!! - SUCCESS CASE 
[ self performSelector:@selector(longRunningOp) withObject:nil afterDelay:0.4 ]; 

これは、なぜこの動作が正当な理由で行われたかについての私の単純な試みです。

私のRunMode呼び出しでは、現在のループを使用しているので、この呼び出しは現在のループの設定方法によって異なります。現在のループは、dispatch_asyncを使用してperformSelector:withObject:afterDelayを使用するかどうかに基づいて、トップレベルで異なる設定になっているようです。さらに、ナビゲーションバーをクリックするとperformSelector:withObject:afterDelay呼び出しが発生し、トップレベルのスタックをディスパッチ非同期にするよりも望ましい状態になります。
私はその最高の説明を疑う。しかし、その瞬間に私は最高です。

2

スタック上でCFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUEが表示された場合、実行ループはメインのディスパッチキューを「処理中」です。つまり、メインキューにキューされているすべてのブロックが実行されています。これらのブロックの1つは、viewDidAppearメッセージの送信を終了します。だから、スタックは次のようになります。

-[ViewController viewDidAppear:] 
-[UIViewController _setViewAppearState:isAnimating:] 
-[UIViewController _endAppearanceTransition:] 
-[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] 
__49-[UINavigationController _startCustomTransition:]_block_invoke 
-[_UIViewControllerTransitionContext completeTransition:] 
__53-[_UINavigationParallaxTransition animateTransition:]_block_invoke95 
-[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] 
-[UIViewAnimationState sendDelegateAnimationDidStop:finished:] 
-[UIViewAnimationState animationDidStop:finished:] 
CA::Layer::run_animation_callbacks(void*) 
_dispatch_client_callout 
_dispatch_main_queue_callback_4CF 
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ 
__CFRunLoopRun 
CFRunLoopRunSpecific 
GSEventRunModal 
UIApplicationMain 
main 
start 

viewDidAppear:に、あなたがメインディスパッチキューに別のブロックを入れて、スタックは次のようになりますので、あなたは、再帰的にメイン実行ループを実行することとします

__CFRunLoopRun 
CFRunLoopRunSpecific 
-[NSRunLoop(NSRunLoop) runMode:beforeDate:] 
-[ViewController viewDidAppear:] 
-[UIViewController _setViewAppearState:isAnimating:] 
-[UIViewController _endAppearanceTransition:] 
-[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] 
__49-[UINavigationController _startCustomTransition:]_block_invoke 
-[_UIViewControllerTransitionContext completeTransition:] 
__53-[_UINavigationParallaxTransition animateTransition:]_block_invoke95 
-[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] 
-[UIViewAnimationState sendDelegateAnimationDidStop:finished:] 
-[UIViewAnimationState animationDidStop:finished:] 
CA::Layer::run_animation_callbacks(void*) 
_dispatch_client_callout 
_dispatch_main_queue_callback_4CF 
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ 
__CFRunLoopRun 
CFRunLoopRunSpecific 
GSEventRunModal 
UIApplicationMain 
main 
start 

今度は、この再帰呼び出しで実行ループがメインディスパッチキューに再びサービスする必要があると思われるようです。しかし、それはできません。メインのディスパッチキューはの連続したキューです。つまり、一度に複数のブロックを実行することはありません。実行中のメインキューブロックは、すでにCA::Layer::run_animation_callbacks(void*)と呼ばれています。そのブロックが戻るまで他のメインキューブロックは開始できません。実行ループはこれを知っているため、この再帰呼び出しでメインキューを処理しようとしません。

あなたの質問に投稿したコードは実際には何の仕事もしていないので、具体的な助けを与えるのは難しいです。しかし、あなたはOpenGLについて言及しました。 AppleのOpenGL ES Programming Guide for iOSには、「並行処理とOpenGL ES」という章があり、処理をバックグラウンドスレッドまたはキューに移動する方法について説明しています。

+0

非常に優れた分析をお寄せいただきありがとうございます。あなたは正しい - それはまさにそのスタックだったのです!しかし、何かが私を悩ませる。私はdispatch_afterを実行したときに、なぜそれがタイマーにキューイングされず、スタックを巻き戻してからスケジュールしますか?それでデッドロックは起こりません。私がperform_selectorを実行するとうまくいくので、それはうまくいくと思いますが、dispatch_afterのためのものではありません。 – nishant

関連する問題