上文回顾:「回绝踩坑」仅有一种阻拦 WKWebView 资源恳求的方法

布景

在上文,笔者介绍了如何阻拦资源恳求。但文中有些疏漏之处,也有读者在评论区里提到了,会遇到This task has already been stopped这样的溃散,导致溃散率变高。但咱们其实已经经过撤销恳求机制处理了大部分,常规运用上,这个溃散就没再遇到,导致忽视了。

这次也是经过线上溃散剖析,发现仍是有相似的溃散呈现。经过堆栈剖析,锁定到如下代码:

「回绝踩坑」阻拦 WKWebView 恳求后呈现溃散的原因

实质原因

闲话少说,先来聊一下呈现这个溃散的实质原因是什么:

究其根本,是因为WKWebView和咱们本身的 App 所属2个不同的进程,这儿的WKURLSchemeTask实质上是进程间通讯的句柄。

进程间通讯是一个耗时操作,且不能确保连接的两边生命周期共同。这不是线程通讯,咱们能够经过引用计数来持有目标。

这就导致,在传输前或者传输中,经过WKURLSchemeTask传输时,WKWebView所在进程被开释,就会导致呈现This task has already been stopped反常抛出溃散。

问题剖析

传输前

如果是WKURLSchemeTask传输前,咱们在上文中已经做了防护:

经过体系供给的有限的2个署理方法,咱们能够拿到中止的机遇:

- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
    [task cancel]; // 这儿执行任务中止动作
}

传输中(本次遇到)

在传输前阻拦已经能够杜绝大部分溃散情况, 但有一种情况会导致在传输中发生溃散:

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    ...
    [self.schemeTask didReceiveData:data]; // 溃散呈现在这儿
}

原因是回来的数据是分片的,这儿的data不是完好数据,署理方法会分片回来多次data

复现方法也被咱们找到了:在加载大文件时,立刻退出WKWebView,极易呈现该问题。

这在体系规划上也是合理的,但现象是咱们的WKURLSchemeTask数据传输无法杰出的停下来,咱们尝试过在[self.schemeTask didReceiveData:data]前添加各种isCanceled的判别也然并卵。

经过排查发现,溃散的不是当次的传输,而是上一次未完成的传输。上文也说道,这个进程间通讯是耗时操作,在数据量小的情况下极不易发生溃散,而数据量大(都发生分片了)的情况下,就很有可能在传输过程中发生中断,导致溃散。

处理计划

计划一:持有WKWebView确保它生命周期

经过上文的剖析,咱们知道原因是WKWebView的开释让WKURLSchemeTask猝不及防。

那一个简单的思路就是咱们让WKWebView开释不要那么及时。

做法上,不建议让恳求阻拦与WKWebView实例耦合,这样会破坏单一职责准则。

好的做法是构建WKWebView容器池,运用容器复用的方法,不再开释容器,而是循环运用当时的容器,天然就避免了这个溃散的发生。

计划二:添加 try catch

当然,尽管咱们了解了问题的实质,但也能够头疼医头、脚疼医脚的方法处理问题。

咱们发现它尽管是溃散,但它好在是一个被抛出的Exception,而不是内存指针溃散。

那尽管基本没人在 iOS 上运用try catch,但确实能处理这个问题。

    @try {
        [self.schemeTask didReceiveData:data];
    } @catch (NSException *exception) {}
    ...
    @try {
        [self.schemeTask didReceiveResponse:response];
        completionHandler(NSURLSessionResponseAllow);
    } @catch (NSException *exception) {
        completionHandler(NSURLSessionResponseCancel);
    }
    ...
    @try {
        if (error != nil) {
            [self.schemeTask didFailWithError:error];
        } else {
            [self.schemeTask didFinish];
        }
    } @catch (NSException *exception) {
    } @finally {
        ...
    }

最好在所有运用WKURLSchemeTask的当地都加上try catch

当然,添加了try catch会导致打包后体积略微增大,但对比处理线上溃散而言,这仍是性价比比较高的。

其他计划

网上其实还有其他的处理计划,但或多或少不适合咱们全面铺开运用的场景。

避免分片传输

网上很多处理计划都是用的如下方法:

if (self.tasks[urlSchemeTask.description]) {
      if (error) {
          [urlSchemeTask didFailWithError:error];
      } else {
          [urlSchemeTask didReceiveResponse:response];
          [urlSchemeTask didReceiveData:data];
          [urlSchemeTask didFinish];
      }
 }

如果当时任务还存在,就整体执行传输操作,不再把data分片传输。

这尽管能够处理问题,但破坏了体系的规划,遇到大文件传输,会下降体验和劣化功能。

总结

体系供给的阻拦方法真的太少了,但凡能供给一个安全调用的方法也好。但也能够从旁边面阐明,iOS 根本不想咱们去做阻拦操作,甚至不推荐运用WebView

但现实上跨端交融的趋势是不可避免的,阻拦也是功能优化的一部分。从上文的评论区能够看到,遇到阻拦问题的同学也不少。

仍是期望大家能少踩坑 ~


感谢阅读,如果对你有用请点个赞 ❤️

「回绝踩坑」阻拦 WKWebView 恳求后呈现溃散的原因
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。