概述:当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);
}
以上只要有一端做修改即可。