6

WebKit でデッドロックを引き起こしていると思われる問題を発見しました。このコードをメイン スレッドから実行すると、正しくアラートが表示されます。アラートの「OK」ボタンをタップすると、アラートが消え、すべてが正常に機能しています。

[theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"];

わずかな変更を加えると、アラート メッセージは引き続き表示されますが、[OK] ボタンをタップすることはできません。アラートを無視することはできず、アプリに侵入するとstringByEvaluatingJavaScriptFromString通話がハングします。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"];
    });
});

これら 2 つの唯一の違いは、2 番目のものでは、ディスパッチ キューのコンテキストでメイン スレッドで JS を実行していることです。

一方、次のようにすると、ハングは発生しません。

- (void) showHi:(id) it
{
    [(UIWebView*)it stringByEvaluatingJavaScriptFromString:@"alert('hi');"];
}

....

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];
});

誰かがハングを引き起こす原因に光を当てることができますか?

編集:

関連する質問:

dispatch_async または performSelectorOnMainThread を使用してメイン スレッドで UI の変更を実行しますか?
メインキューの performSelectorOnMainThread と dispatch_async の違いは何ですか?
Grand Central Dispatch (GCD) と performSelector - より詳しい説明が必要

非常によく似た質問:

UIWebView stringByEvaluatingJavaScriptFromString は、GCD を使用して呼び出すと、iOS5.0/5.1 でハングします。

4

3 に答える 3

4

これは単に UIWebView のバグのようです。この質問によると、iOS 5 で導入され、iOS 4.3 以下ではデッドロックしませんでした。

興味深いことに、呼び出しの直前に UIAlertView を提示すると、stringByEvaluatingJavaScriptFromString:奇妙なことにデッドロックが回避されます。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"Test"
                                                          message:@"Test"
                                                         delegate:nil
                                                cancelButtonTitle:@"OK"
                                                otherButtonTitles:nil];
        [message show];

        [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"];
    });
});

ただし、私の理論は次のとおりです。デッドロックが発生した後に実行を一時停止すると、WebThread が で停止していることがわかり__psynch_mutexwaitます。JavaScript エンジンは異なるスレッドで実行されるため、アラート ビューを表示するようにメイン スレッドに指示する必要があります。ただし、stringByEvaluatingJavaScriptFromString:値を返すブロッキング呼び出しです。[OK] をクリックしてアラートを閉じた場合にのみ、値を返すことができます。ここでデッドロックが発生しているように見えます: 別のスレッドからメイン スレッドに Web ビューに JavaScript を実行するよう指示し (これはさらに別のスレッドで発生します)、次にメイン スレッドにアラート ビューを表示するよう指示します。 [OK] をクリックすると、その戻り値が JavaScript に返されます。そして、呼び出しがstringByEvaluatingJavaScriptFromString:返されたときにのみ、GCD に渡したブロックが完了します。

しかし、それはバグでなければなりません。最初に UIAlertView を表示するとデッドロックが発生しないのが不思議です。その場合、おそらく iOS は何らかのキューに 2 番目のアラート ビューを配置し、デッドロックを防ぎます。奇数!

于 2013-10-30T15:37:13.183 に答える