2013-10-23 123 views
6

我發現了一個似乎導致WebKit出現死鎖的問題。如果我從主線程運行此代碼,我會正確地看到警報。我可以點擊進入戒備狀態「確定」按鈕,解聘和一切工作正常:GCD和webView造成的死鎖

[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');"]; 
    }); 
}); 

在這兩個不同的是,在第二個,它正在運行的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]; 
}); 

有人可以照到什麼錯誤造成掛一些輕?

編輯:

相關問題:

Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?
Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?
Grand Central Dispatch (GCD) vs. performSelector - need a better explanation

非常類似的問題:

UIWebView stringByEvaluatingJavaScriptFromString hangs on iOS5.0/5.1 when called using GCD

回答

4

這似乎只是UIWebView中的一個錯誤。根據this question,它在iOS 5中引入,並且在iOS 4.3及更低版本上確實執行了而不是死鎖。

有趣的是,在調用stringByEvaluatingJavaScriptFromString:奇怪的是前右呈現UIAlertView中防止死鎖:

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:是返回值的阻止調用。只有通過單擊確定解除警報後,才能返回該值。這就是看似發生死鎖的地方:從另一個線程中,我們告訴主線程告訴Web視圖運行JavaScript(這發生在另一個線程上),這反過來告訴主線程顯示一個警報視圖,它只能一旦確定被點擊,將其返回值返回給JavaScript。並且只有當stringByEvaluatingJavaScriptFromString:返回的呼叫是我們傳遞給GCD的塊完成。

雖然它一定是個bug。奇怪的是,當我首先顯示一個UIAlertView時,死鎖不會發生。也許iOS在這種情況下將第二個警報視圖放在某種類型的隊列中,這可以防止死鎖。奇!

5

我認爲這是在webviewImage類引用說明現在

,爲了您的死鎖的情況下,你可以通過

performSelector:withObject:afterDelay:inModes:更換此 [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];

模擬相同。 默認情況下,它的模式是NSDefaultRunLoopMode,它本質上是原子的,而在非原子的情況下,您必須更改線程的當前調度模式。

我希望它保持翔實。此外,歡迎提供更多建議。

+0

你能解釋一下關於運行循環模式的更多信息,以及'dispatch_async'如何執行將導致代碼在主線程中運行?另外,你如何改變線程的調度模式? –

+0

@技術人員 - 我面臨同樣的問題。錯誤是無效SendDelegateMessage(NSInvocation *):委託(webView:identifierForInitialRequest:fromDataSource :)等待10秒後未能返回。主要運行循環模式:kCFRunLoopDefaultMode並阻止UI。 WebView凍結。所以你有什麼想法解決這個問題的最好方法是什麼。 – Sandeep

0

有幾件事情可能是這方面的一個因素。正如其他人所說的,JavaScript並不在主線程上執行,而是在自己的後臺線程上執行。你可能已經注意到你的代碼的執行一直等到JavaScript完成。我不知道代碼中的具體實現,但它聽起來像是在內部使用dispatch_syncdispatch_barrier_sync

您應該注意的下一項是UIAlertView -show和JavaScript alert(..)提供的警報之間存在顯着差異。 UIAlertView -show異步運行。您可以這樣說,因爲在解除警報之前,在-show之後代碼執行會繼續。如果您在JavaScript中執行相同的實驗,代碼執行將停止,直到警報關閉。

最後,調度隊列和線程之間是有區別的。你可以在this thread瞭解更多。我懷疑JavaScript執行正在執行[NSThread isMainThread],並且它報告NO,因爲它位於主隊列中,而不是主線程。由於此檢查正在報告NO,因此主隊列會執行dispatch_sync,而主隊列已被阻塞,正在等待JavaScript隊列。