作為一名開發(fā)人員,如何定位解決線上Crash是每一個必修的課題搂蜓。那問題來了,Crash 如何產(chǎn)生无畔?作為一名 iOS 開發(fā),我今天主要分享 iOS Crash 相關(guān)的內(nèi)容
Crash 產(chǎn)生
Crash 產(chǎn)生一般分兩種情況:
- 應(yīng)用程序自己調(diào)用退出函數(shù)吠冤。eg:調(diào)用自殺函數(shù) kill()
- 系統(tǒng)把你應(yīng)用殺死浑彰。eg:應(yīng)用內(nèi)存占用過高時,系統(tǒng)保護機制會把你的應(yīng)用干掉拯辙。
(各種崩潰場景后續(xù)再討論)
想清楚以上兩個場景之后我們我們來看幾個元兇郭变。
- 軟件異常
軟件異常主要來源于兩個 API 的調(diào)用 kill() 、 pthread_kill() , 而 iOS 中我們常常遇到的 NSException 未捕獲涯保、 abort() 函數(shù)調(diào)用等诉濒,都屬于這種情況。比如我們诚Υ海看到 Crash 堆棧中有 pthead_kill 方法的調(diào)用未荒。
(此刻我知道你的內(nèi)心活動,**從來沒掉過啊及志。沒錯你是沒掉片排,但是系統(tǒng)庫會 eg:libc,刺激不刺激速侈,驚喜不驚喜) - 硬件異常
硬件產(chǎn)生的信號始于處理器 trap率寡,處理器 trap 是平臺相關(guān)的。出現(xiàn)這種情況倚搬,計算機會暫停當(dāng)前程序勇劣,及時轉(zhuǎn)入故障處理。比如我們遇到的野指針崩潰大部分是硬件異常潭枣。 - Mach異常
這里雖然叫異常比默,但是要和上面的兩種分開來看。我們了解到蘋果的內(nèi)核 xnu 的核心是 Mach , 在 Mach 之上建立了 BSD 層盆犁∶溃“Mach異常” 是 “Mach異常處理流程” 的簡稱谐岁。不懂沒事醋奠,后面有講解
除了以上三個大殺器,還有一個是程序語言異常伊佃,這個在我們的Crash日志也是比較常見的窜司,但是與以上三個不同,這種異常往往是我們代碼邏輯不合理造成的航揉。沒錯塞祈,說的就是看文章的你,為啥會崩潰帅涂,你心里一點數(shù)都沒有嗎议薪?尤蛮??斯议?這種稍后我也會給大家總結(jié)
接下來我們看一下 Mac OS & iOS 是如何處理這些異常的产捞。以下內(nèi)容來自“深入解析 Mac OS X & iOS 操作系統(tǒng)”
軟件異常處理:
硬件異常處理:
通過上面兩張圖,我們可以很清楚的看到哼御,無論是軟件異常坯临,還是硬件異常,最終都會被轉(zhuǎn)換為信號恋昼,然后通過act_set_astbsd()發(fā)送給我們的應(yīng)用進程尿扯,喚醒其中的某個線程響應(yīng)指定操作(記住這句話,我們后續(xù)有大用)焰雕。此時的信號是 UNIX 信號,如 SIGBUS SIGSEGV SIGABRT SIGKILL 等芳杏。此時大家可以去翻翻后臺的Crash日志矩屁,你會發(fā)現(xiàn)好多這中以SIG開頭的Crash。具體信號的含義大家可以自行 Google爵赵,或者直接點擊文章參考參考查看吝秕。
UNIX信號拋出簡單流程大致如下:
附 “Mach異常” 與 “UNIX信號” 的轉(zhuǎn)換關(guān)系代碼空幻,來自 xnu 中的 bsd/uxkern/ux_exception.c :
switch(exception) {
case EXC_BAD_ACCESS:
if (code == KERN_INVALID_ADDRESS)
*ux_signal = SIGSEGV;
else
*ux_signal = SIGBUS;
break;
case EXC_BAD_INSTRUCTION:
*ux_signal = SIGILL;
break;
case EXC_ARITHMETIC:
*ux_signal = SIGFPE;
break;
case EXC_EMULATION:
*ux_signal = SIGEMT;
break;
case EXC_SOFTWARE:
switch (code) {
case EXC_UNIX_BAD_SYSCALL:
*ux_signal = SIGSYS;
break;
case EXC_UNIX_BAD_PIPE:
*ux_signal = SIGPIPE;
break;
case EXC_UNIX_ABORT:
*ux_signal = SIGABRT;
break;
case EXC_SOFT_SIGNAL:
*ux_signal = SIGKILL;
break;
}
break;
case EXC_BREAKPOINT:
*ux_signal = SIGTRAP;
break;
}
看了以上內(nèi)容烁峭,我想你的內(nèi)心一定是
老實說以上理解以上內(nèi)容確實需要你有相當(dāng)?shù)挠嬎銠C基礎(chǔ)功力才行,為了照顧你的心情秕铛,接下來咱們聊點你能看懂的约郁。
除了上面咱們說道的,還有一種程序語言異常但两,這種異常通常是程序語言自己封裝好的鬓梅。產(chǎn)生的原因通常是因為程序員編寫邏輯錯誤造成的,沒錯谨湘,就是你自己造的孽绽快。作為iOS程序員,我們主要是關(guān)注的是 Objec-C 和 Swift紧阔。
Object-C 的異常的異常主要是NSException對象封裝的坊罢,比較多,咱們這里看幾個比較常見的擅耽,文末有參考中有鏈接大家可以看到所有的異常類型活孩。
先來個王炸,這個你肯定見過:
-
NSInvalidArgumentException
傳遞非法參數(shù)給一個方法時拋出的異常乖仇。
常見場景:- NSNutableDictionaryr操作key或value的函數(shù)诱鞠,如setObject:forKey:挎挖、removeObjectForKey等等。
- NSMutableArray操作value的函數(shù)航夺,如addObject:蕉朵、 insertObject:atIndex:等等。
- NSString操作函數(shù)阳掐,如initWithString:始衅、initWithFormat:、stringWithString:等等缭保。
這里要住意多線程操作汛闸,這個坑,深到一半人爬不出來艺骂,比如我前面的小朋友诸老,直接被搞離職了
-
NSRangeException:
嘗試訪問某些數(shù)據(jù)范圍之外時拋出的異常。
常見場景:- NSArray包含索引的操作钳恕,如insertObject:atIndex:别伏、objectAtIndex:等等。
- NSString包含索引的操作忧额,如characterAtIndex:厘肮、getCharacters:range:等等。
-
NSFileHandleOperationException
如果嘗試確定文件句柄類型失敗或嘗試讀取文件或通道失敗睦番,則會拋出此異常类茂。
常見場景:- 空間不足:會提示No space left on devie。
- 沒有讀寫權(quán)限:會提示Bad file descriptor托嚣。
- 讀文件失敗巩检。
在操作文件時,要驗證文件句柄的有效性示启,對文件大小進行校驗碴巾,對存儲空間進行判斷。
-
KVO引起的異常:
常見場景:- 多次移除KVO
拋出NSRangeException異常 - 添加或移除時keyPath參數(shù)為nil
- 沒有實現(xiàn)observeValueForKeyPath方法(非必顯丑搔, iOS14)
我在當(dāng)前 iOS 15的系統(tǒng)測試過厦瓢,無論是沒有移除觀察者還是VC推出時沒有一處觀察者都沒出現(xiàn)崩潰。多次移除相同Path時和path名字為nil會出現(xiàn)Crash
- 多次移除KVO
-
iOS Crash之NSMallocException
常見場景:- 分配的空間過大
- 圖像占用空間過大
- OOM問題啤月。這個主要是程序死循環(huán)造成的
-
NSGenericException
常見場景:- 可變對象遍歷過程中發(fā)生了改變煮仇。
以上大概就是我們開發(fā)和線上應(yīng)用常見一些異常。至于swift谎仲,應(yīng)為NSexception是Cocoa框架的浙垫,swift也繞不過,所以上面的異常,swift也是都有的夹姥,當(dāng)然由于語言上的設(shè)計優(yōu)勢杉武,swift出現(xiàn)的可能行降低一些。swift 該只有 try! 解析空辙售。其他歡迎大家補充轻抱。
畢竟是初始 iOS Crash,就給大家分析到這里吧旦部,下一篇我們看一下crash收集和符號化祈搜。
參考文檔:
處理器陷阱 。
UNIX 信號
iOS Crash 分析策略
野指針
理解異常類型
Object-C Exception
iOS內(nèi)功篇:淺談Crash