在iOS開發中,Crash無疑是App的致命殺手。作為一個嚴謹的iOS 開發人員來說,寫出優秀的健碩的無Crash代碼至關重要。但是隨著工程代碼量的提升,功能的迭代,以及協作開發的模式,難免會有Crash的發生。在發生Crash時,我們應迅速定位問題,解決問題,將Crash幾率降到最低。
Crash 收集
當程序運行發生Crash的時候,系統會把運行的最后時刻的程序運行記錄保存下來,存儲到一個.crash文件中,也就是我們常說的Crash日志。
一、 在開發中,最常遇到的Crash就是Debug狀態下了,此時我們是幸運的,因為這個bug我們自己最先知道,我們可以在別人發現它之前把它改好。而且,Xcode 會提供給我們最明確的Crash信息,直接定位到Crash 的那行代碼,并會打印出Crash Reason 及調用棧信息。
二、如果我們運氣稍差一些,在開發中自測App沒有發現任何Crash問題,但是測試童鞋或者其他部門的童鞋在測試使用中發現了Crash,但愿不要被老板發現o(╯□╰)o。無論此時的crash是必現還是非必現,我們都可以拿到測試童鞋的Crash設備,拿到設備導出Crash日志吧~
三、最難過的,無疑是自測沒發現,測試童鞋也沒發現,但是群眾的眼睛的雪亮的,我們親愛用戶遇到了這個棘手的Crash,這極差的用戶體驗很有可能讓用戶粉轉黑,怎么辦?做一個Crash收集器勢在必行!!!~
關于Crash收集的框架,已經有比較成熟的開源框架,KSCrash、CrashKit等,也有第三方的Crash統計產品,如Crashlytics,Hockeyapp,友盟,Bugly等等。
當App發生Crash時要上傳Crash日志,之后我們可以通過服務端自己的Crash收集器拿到Crash文件,或者借助第三方服務拿到Crash文件。
2 Crash 分析
拿到了Crash日志,我們該從何入手呢?
Crash日志會提供給我們很多信息,我們要在其中提取出來可以幫我們快速定位問題的信息。
首先我們看到這幾行信息,
ncident Identifier:崩潰報告的唯一標識符,不同的Crash日志該標示符也不同。
CrashReporter Key:設備標識相對應的唯一鍵值(并非真正的設備的UDID,蘋果為了保護用戶隱私iOS6以后已經無法獲取)。通常同一個設備上同一版本的App發生Crash時,該值都是一樣的。
Hardware Model :代表發生Crash的設備類型。
Process:代表系統Crash的進程名稱,通常都是我們的App的名字, [ ]里面是當時進程的ID。
Path:App的所在路徑。
Identifier:我們App的Indentifier,通常為“com.xxx.yyy”,xxx代表公司的域名,yyy代表某一個App標識。
AppVersion:當前App的版本號,由Info.plist中的兩個字段組成,CFBundleShortVersionString and CFBundleVersion。
Code Type:當前App的CPU架構。
Parent Process:當前進程的父進程,由于iOS中App通常都是單進程的,一般父進程都是launchd。
Date/Time:發生crash的時間
Launch Time:啟動App的時間
OS Version:iOS系統固件版本
Report Version:日志版本
Exception Type: 這個信息非常重要,它就像是這個crash的名字,我們知道了它的名字,解決它還難嗎?
Exception Subtype:它就是crash的小名,當它的大名滿足不了我們的時候,google它的小名,你一定會有收獲!~
Triggered by Thread: 問題發生的thread
我們再來看線程信息,在日志中找到crash thread,問題就發生在這里,
有些情況下,Crashed Thread 的調用棧中會明確的告訴我們是執行到哪個類中哪行代碼時發生了問題,這種情況下我們很容易判斷問題原因以及修改問題。但是大多數情況下,調用棧里顯示我們Crash在了一個系統的庫里,我們看不到代碼,所以沒辦法確定是哪里的操作造成了問題,于是,我們需要做點事情,將crash日志文件符號化。
為了解析crash日志,我們需要三個東西:
1.crash文件
2.符號文件:.dsymb格式
3.應用程序文件:.app格式
然后我們需要把這三個文件放到同一目錄下,用atos命令來符號化crash日志的某一行:
打開終端,輸入
xcrun atos -o appName.app/appName -arch armv7
然后再輸入你要符號化的那一行后面的調用棧地址,例如:0x000000018a650b38
這樣就可以得到結果:
就能夠定位到具體是代碼的哪一行發生了問題。
更多符號化crash文件的方法,可參考鏈接。http://wufawei.com/2014/03/symbolicating-ios-crash-logs/
3 Crash 處理
一、Watchdog timeout
Exception Code:0x8badf00d
可以讀作“eat bad food”,我吃了壞東西,不能繼續為你工作了。是不是很形象?
當我們的App 在啟動、退出、或者在響應系統事件的時候等待了太長時間,系統會直接殺死進程。Its Not A Crash~
我們應該查看App是否在主線程請求了網絡,或者其他耗時的事情卡住了正常初始化流程。
通常系統允許一個App從啟動到可以相應用戶事件的時間最多為5S,如果超過了5S,App就會被系統終止掉。在Launch,resume,suspend,quit時都會有相應的時間要求。在Highlight Thread里面我們可以看到被終止時調用到的位置,xxxAppDelegate加上行號。
PS. 在連接Xcode調試時為了便于調試,系統會暫時禁用掉Watchdog,所以此類問題的發現需要使用正常的啟動模式。
二、用戶強制退出
Exception Codes: 0xdeadfa11, deadfall
與正常退出殺死App不同,這種情況可能是用戶強制關機或系統強制關機等造成。
三、低內存閃退
當系統發生低內存閃退時,很有可能我們拿不到任何的Crash信息日志,但App的的確確是閃退了。好好做下檢討吧,是不是哪里有內存泄露?用工具好好測試下。
如果我們能夠拿到日志,會發現它和一般的Crash日志不太一樣,通常有Free pages,Wired Pages,Purgeable pages,largest process 組成,同時會列出當前系統調用棧信息。
如果我們用的是MRC,首先靜態分析一下,是不是哪里忘記了release ? dealloc 寫的是否正確? 然后使用Instruments檢查下內存使用情況,看看哪里的內存占用較高?是否內存泄露?通常大量的圖片不能及時釋放內存空間的時候會使內存占用飚升。
內存警告通常在我們debug的時候就會發現,及時的清理掉不用的內存,否則內存占用越來越高,超過系統限制就會被系統殺死。
四、Crash due to bugs
因為程序bug導致的Crash通常千奇百怪,很難一概而論。大部分情況通過Crash日志就可以定位出問題,當然也不排除部分疑難雜癥看半天都不值問題出在哪兒。這個就只能看功底了,一點點找,總是能發現蛛絲馬跡。是在看不出來時還可以求助于Google大神,總有人遇到和你一樣的Bug
五、Exception Type
1)EXC_BAD_ACCESS
此類型的Excpetion是我們最長碰到的Crash,通常用于訪問了不改訪問的內存導致。一般EXC_BAD_ACCESS后面的"()"還會帶有補充信息。
SIGSEGV:通常由于重復釋放對象導致,這種類型在切換了ARC以后應該已經很少見到了。
SIGABRT:收到Abort信號退出,通常Foundation庫中的容器為了保護狀態正常會做一些檢測,例如插入nil到數組中等會遇到此類錯誤。
SEGV:(Segmentation Violation),代表無效內存地址,比如空指針,未初始化指針,棧溢出等;
SIGBUS:總線錯誤,與 SIGSEGV 不同的是,SIGSEGV 訪問的是無效地址,而 SIGBUS 訪問的是有效地址,但總線訪問異常(如地址對齊問題)
SIGILL:嘗試執行非法的指令,可能不被識別或者沒有權限
2)EXC_BAD_INSTRUCTION
此類異常通常由于線程執行非法指令導致。
1.在代碼中修改了storyboard與outlet的對應關系,但是storyboard沒有更新時發生過此crash。
2.與第三方庫中方法沖突時發生過此crash。
3.調用系統方法時傳入了不恰當的指針類型。
3)EXC_ARITHMETIC
代碼中做除法時分母為零了會發生此問題。
6、Exception Code
0xbaaaaaad此種類型的log意味著該Crash log并非一個真正的Crash,它僅僅只是包含了整個系統某一時刻的運行狀態。通常可以通過同時按Home鍵和音量鍵,可能由于用戶不小心觸發
0xbad22222當VOIP程序在后臺太過頻繁的激活時,系統可能會終止此類程序
0x8badf00d這個前面已經介紹了,程序啟動或者恢復時間過長被watch dog終止
0xc00010ff程序執行大量耗費CPU和GPU的運算,導致設備過熱,觸發系統過熱保護被系統終止
0xdead10cc程序退到后臺時還占用系統資源,如通訊錄被系統終止
0xdeadfa11**前面也提到過,程序無響應用戶強制關閉
更多開發中遇到的錯誤碼可以參考鏈接https://en.wikipedia.org/wiki/Hexspeak