iOS 之webview 的js交互(alert、confirm、prompt)弹窗造成界面卡死

概述:当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()造成卡死的原因如下:

iOS 之webview 的js交互(alert、confirm、prompt)弹窗造成界面卡死_第1张图片
dispatch_async的实现代码.jpg

上图是dispatch_async()的实现代码
dispatch_atomic_incdispatch_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);
}

以上只要有一端做修改即可。

你可能感兴趣的:(iOS 之webview 的js交互(alert、confirm、prompt)弹窗造成界面卡死)