概述:當iOS用戶端中webView 與js互動,在主線程執行js腳本時,而js腳本存在alert()、confirm()、prompt()這三種彈窗時會造成iOS界面卡死。
1、造成卡死時的代碼如下:
NSString *script = "doSubmit()";
(1)
iOS端實作:
[self.webView stringByEvaluatingJavaScriptFromString:script];
js實作:
doSubmit = function (obj) {
if (confirm("已存在故障回報,确認繼續送出?")){
flag = true;
} else {
flag = false;
}
return flag;
}
(2)
iOS端實作:
dispatch_async(dispatch_get_main_queue(), ^{
[self.webView stringByEvaluatingJavaScriptFromString:script];
});
js實作:
doSubmit = function (obj) {
if (confirm("已存在故障回報,确認繼續送出?")){
flag = true;
} else {
flag = false;
}
return flag;
}
(3)
iOS端實作:
[self.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:script waitUntilDone:YES];
js實作:
doSubmit = function (obj) {
if (confirm("已存在故障回報,确認繼續送出?")){
flag = true;
} else {
flag = false;
}
return flag;
}
2、卡死原因分析:
(1)第一種情況由于iOS端的js腳本是在主線程執行,而js的三種彈窗上的按鈕操作也必須在主線程完成,alert()、confirm()、prompt()這三種彈窗會中斷js腳本的執行,也就是彈窗上面的按鈕沒有點選響應結果前,if (confirm("已存在故障回報,确認繼續送出?"))後面的代碼将不會執行,此時 [self.webView stringByEvaluatingJavaScriptFromString:script];占用着主線程一直在等待return flag;語句執行完成。這将造成:點選彈窗的按鈕在等js執行return 語句拿到主線程重新整理UI,而占用主線程的一直在等js腳本執行完成的互相等待。
(2)第二種dispatch_async()造成卡死的原因如下:

dispatch_async的實作代碼.jpg
上圖是dispatch_async()的實作代碼
dispatch_atomic_inc和dispatch_atomic_dec是原子操作函數;
由上圖可知dispatch_async執行任務的時候會對目前工作的線程加原子操作,而原子操作是不會被線程排程機制打斷的操作;這種操作一旦開始,就一直運作到結束,中間不會有任何 的線程切換, 任務不執行完, 工作線程是不會被中斷的。是以主線程執行的js腳本不會被中斷。
js中彈窗中斷代碼的作用, 彈窗下面的代碼在彈窗上面的按鈕點選之前是不會被執行到的。是以它們進入了互相等待資源中。
(3)
performSelectorOnMainThread: withObject: waitUntilDone:
這個方法中waitUntilDone:的參數官方注釋wait的意思:
A Boolean that specifies whether the current thread blocks until after the specified selector is performed on the receiver on the main thread.
Specify YES to block this thread;
otherwise, specify NO to have this method return immediately.
如果wait是YES,會鎖死主線程,直到selector方法執行完. 如果為NO,selector方法會立即傳回。
而iOS 用戶端實作的代碼是YES,是以存在競争主線程,造成界面卡死的問題。
3、卡死問題處理:
或:iOS端代碼實作修改:
[self.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:script waitUntilDone:NO];
或:js代碼實作修改:
doSubmit = function (obj) {
setTimeout(function() {
if (confirm("已存在故障回報,确認繼續送出?")){
flag = true;
} else {
flag = false;
}
return flag;
},-1);
}
以上隻要有一端做修改即可。