在這里主要分析使用UIWebView導(dǎo)致產(chǎn)生crash的原因倚聚。
從我們項(xiàng)目的crash日志收集平臺中,有諸多如下的日志:
Application received signal 11
1 libobjc.A.dylib 0x1800bc150 _objc_msgSend + 16
2 UIKit 0x18799156c -[UIWebView webView:resource:didFinishLoadingFromDataSource:] + 84
3 CoreFoundation 0x18164ce80 ___invoking___ + 144
4 CoreFoundation 0x1815422c4 -[NSInvocation invoke] + 292
5 CoreFoundation 0x181546e9c -[NSInvocation invokeWithTarget:] + 60
6 WebKitLegacy 0x1873d4820 <redacted>
Application received signal 11
崩潰線程
WebThread
格式化
1 libobjc.A.dylib 0x1800bc150 _objc_msgSend + 16
2 UIKit 0x187c33354 -[UIWebView webThreadWebView:resource:willSendRequest:redirectResponse:fromDataSource:] + 92
3 WebKitLegacy 0x187477e60 <redacted>
4 WebKitLegacy 0x1873d315c <redacted>
5 WebCore 0x1861cdee0 WebCore::ResourceLoadNotifier::dispatchWillSendRequest(WebCore::DocumentLoader*, unsigned long, WebCore::ResourceRequest&, WebCore::ResourceResponse const&) + 232
6 WebCore 0x186f1c494 WebCore::ResourceLoader::willSendRequestInternal(WebCore::ResourceRequest&, WebCore::ResourceResponse const&) + 556
7 WebCore 0x18703cbb0 WebCore::SubresourceLoader::willSendRequestInternal(WebCore::ResourceRequest&, WebCore::ResourceResponse const&) + 252
8 WebCore 0x1861cd040 WebCore::ResourceLoader::init(WebCore::ResourceRequest const&) + 288
9 WebCore 0x1861ccdec WebCore::SubresourceLoader::startLoading() + 36
10 WebKitLegacy 0x1874b23b0 <redacted>
以前在看到這種日志的時候凿可,就在想惑折,signal 11
崩潰日志不明確,或者說WebView的系統(tǒng)代碼枯跑,不是我們自己的代碼改不了的種種惨驶。。敛助。
不過現(xiàn)在從crash統(tǒng)計平臺的數(shù)據(jù)看來數(shù)量相當(dāng)大粗卜,是非常影響用戶的體驗(yàn)的。所以今天就抽時間來分析分析其原因纳击。
從日志上看续扔,基本都是Application received signal 11
攻臀,那么signal 11
是代表什么呢?
于是翻看系統(tǒng)的頭文件sys/signal.h
, 我們可以發(fā)現(xiàn)里面有一個宏定義#define SIGSEGV 11 /* segmentation violation */
, 這個SIGSEGV
其實(shí)就是signal 11
的宏定義纱昧,注釋很明確了:無效的內(nèi)存引用
那么問題就來了刨啸,WebCore內(nèi)部發(fā)出無效內(nèi)存引用,我們好像也無能為力笆洞唷呜投??存璃?咋辦呢?
這時候就去看看UIWebView有沒有其它什么可能的原因雕拼,發(fā)現(xiàn)在頭delegate的定義
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
我們都知道assign
一般是用于元類型纵东,delegate在ARC下一般是被聲明為weak
。
這就奇怪了啥寇,怎么這么聲明呢? 因此帶著疑問去看UIWebView的文檔:
Protocol
UIWebViewDelegate
The UIWebViewDelegate protocol defines methods that a delegate of a
UIWebView
object can optionally implement to intervene when web content is loaded.
SDK
iOS 2.0+
Framework
UIKit
On This Page
Overview
Topics
Relationships
See Also
Overview
> Important
> Before releasing an instance of UIWebView for which you have set a delegate,
> you must first set the UIWebView delegate property to nil before disposing of the UIWebView instance.
> This can be done, for example, in the dealloc method where you dispose of the UIWebView.`
>
意思就是: 在釋放一個你已經(jīng)為其設(shè)過 delegate 的 UIWebView 實(shí)例之前偎球,你首先一定要將該 UIWebView 對象的 delegate 屬性設(shè)為 nil。比如說辑甜,你可以在你的 dealloc 方法中這樣做衰絮。
這時候就恍然大悟了,我們的項(xiàng)目中對WebView的使用并沒有如此!!! 因?yàn)樵赨IWebView的delgate屬性為assign 在被銷毀的時候delegate不會被設(shè)為nil磷醋,導(dǎo)致WebView回調(diào)的時候引用的delegate已經(jīng)是無效的內(nèi)存指針了猫牡,因?yàn)橹羔樦赶虻膬?nèi)存已經(jīng)被釋放,但指針沒有被置空邓线,這也正是文檔里面為什么要重要強(qiáng)調(diào)需要在銷毀前設(shè)置為nil的原因淌友。
這也解釋了為什么crash日志中總是收到莫名的WebView的crash,而且都是Application received signal 11
(即無效的內(nèi)存引用)
到這里骇陈,相信大家都應(yīng)該知道什么原因了震庭,修改 UIWebView 的 delegate 的對象的 dealloc 方法中添加 _webView.delegate = nil; 如下:
- (void)dealloc{
/*
Important
Before releasing an instance of UIWebView for which you have set a delegate,
you must first set the UIWebView delegate property to nil before disposing of the UIWebView instance.
This can be done, for example, in the dealloc method where you dispose of the UIWebView.
*/
if (self.webView.loading) {
[self.webView stopLoading];
}
self.webView.delegate = nil;
}
ps:由于很少寫文章,所以寫出來的都是沒深度沒難度的流水帳你雌。這里也就記錄一下這個過程器联,以便以后遇到類似問題能夠有不至于無從下手。如有錯誤婿崭,望看官們不吝指正拨拓。萬分感激!