1.什麼是崩潰日志,從哪裡能得它?
iOS設備上的應用閃退時,操作系統會生成一個崩潰報告,也叫崩潰日志,保存在設備上。
崩潰日志上有很多有用的信息,包括應用是什麼情況下閃退的。通常,上面有每個正在執行線程的完整堆棧跟蹤信息,所以你能從中了解到閃退發生時各線程都在做什麼,並分辨出閃退發生在哪個線程上。
有幾種方法可以從設備上獲取崩潰日志。
設備與電腦上的iTunes Store同步後,會將崩潰日志保存在電腦上。根據電腦操作系統的不同,崩潰日志將保存在以下位置:
Mac OS X:~/Library/Logs/CrashReporter/MobileDevice/
Windows XP:C:DocumentsandSettings
Windows Vista or 7: C:Users
當用戶抱怨閃退時,你可以要求他讓設備與iTunes同步,並根據操作系統的不同,到上述位置把崩潰日志下載下來,然後通過電子郵件發送給你。
你必需盡量獲取用戶設備生成的所有崩潰日志。因為崩潰日志越多,就越容易診斷問題所在!
通過xcode獲取你自己設備上的日志: 連接設備到電腦, 然後直接用command+shift+2組合鍵打開如下界面
選擇view device logs 進入如下界面, 可以清晰看到所有設備上的crash日志. 找到你的應用名稱就可以看到所有的崩潰信息!
應用提交到App Store後,你也能從 iTunes Connect 獲取到用戶的崩潰日志. 登錄到 iTunes Connect 上, 選擇Manage Your Applications, 點擊相應的應用, 點擊應用圖標下面的View Details按鈕, 然後點擊右欄Links部分的 Crash Reports。
2.什麼情況下會產生崩潰日志?
兩種主要情況會產生崩潰日志:
1.應用違反操作系統規則。
2.應用中有Bug。
違反iOS規則包括在啟動、恢復、掛起、退出時watchdog超時、用戶強制退出和低內存終止。讓我們詳細了解一下吧。
Watchdog 超時機制
從iOS 4.x開始,退出應用時,應用不會立即終止,而是退到後台。但是,如果你的應用響應不夠快,操作系統有可能會終止你的應用,並產生一個崩潰日志。這些事件與下列UIApplicationDelegate方法相對應:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:
上面所有這些方法,應用只有有限的時間去完成處理。如果花費時間太長,操作系統將終止應用。
注意:如果你沒有把需要花費時間比較長的操作(如網絡訪問)放在後台線程上就很容易發生這種情況。關於如果避免這種情況的信息,請參考我們的另外兩篇教程: Grand Central Dispatch 和 NSOperations。
用戶強制退出
iOS 4.x開始支持多任務。如果應用阻塞界面並停止響應, 用戶可以通過在主屏幕上雙擊Home按鈕來終止應用。此時,操作應用將生成一個崩潰日志。
注意:雙擊Home按鈕後,你將看到運行過的所有應用。那些應用不一定是正在運行,也不一定是被掛起。
通常,用戶點擊Home按鈕時,應用將在後台保留約10分鐘,然後操作系統自動將其終止。 所以雙擊Home按鈕顯示的應用列表只是表明那是一系列過去打開過的應用。刪除那些應用的圖標不會產生任何崩潰日志。
低內存終止
子類化UIViewController時,你或許已經注意到didReceiveMemoryWarning方法。
在前台運行的應用擁有訪問和使用內存的最高優化級。然而,這並不意味著該應用能使用設備的所有可用內存 ——每個應用只能使用一部分可用內存。
當內存使用達到一定程度時,操作系統將發出一個 UIApplicationDidReceiveMemoryWarningNotification通知。同時,調用 didReceiveMemoryWarning 方法。
此時,為了讓應用繼續正常運行,操作系統開始終止在後台的其他應用以釋放一些內存。所有後台應用被終止後,如果你的應用還需要更多內存,操作系統會將你的應用也終止掉,並產生一個崩潰日志。而在這種情況下被終止的後台應用,不會產生崩潰日志。
注意:根據蘋果文檔, Xcode不會自動添加低內存日志。你必需手動獲取日志。 然而,根據我的個人經驗,使用 Xcode 4.5.2, 低內存日志也會自動導入,只是”Process”和”Type”屬性都被標為Unknown(未知)。
另外,值得一提的是在極短時間內分配一大塊內存將給系統內存帶來巨大負擔。這樣,也會產生內存警告的通知。
應用中有Bug
如你所想,大多數閃退都是由於應用中有Bug,因此大多數崩潰日志的產生都是因為應用中的Bug。Bug的種類的有很多。
在本教程的後半部分,你將通過調試一個會產生崩潰日志的含有Bug的應用,學習如何找到問題所在並進行修復!
3.崩潰日志的示例
讓我們看看一個崩潰日志的實例,以使你在處理一些實際問題之前心裡有譜。
(1) 進程信息
第一部分是閃退進程的相關信息。
Incident Identifier是崩潰報告的唯一標識符。
CrashReporter Key是與設備標識相對應的唯一鍵值。雖然它不是真正的設備標識符,但也是一個非常有用的情報:如果你看到100個崩潰日志的CrashReporter Key值都是相同的,或者只有少數幾個不同的CrashReport值,說明這不是一個普遍的問題,只發生在一個或少數幾個設備上。
Hardware Model標識設備類型。 如果很多崩潰日志都是來自相同的設備類型,說明應用只在某特定類型的設備上有問題。上面的日志裡,崩潰日志產生的設備是iPhone 4s。
Process是應用名稱。中括號裡面的數字是閃退時應用的進程ID。
接下來幾行不言自明,無需贅述。
(2) 基本信息
這部分給出了一些基本信息,包括閃退發生的日期和時間,設備的iOS版本。如果有很多崩潰日志都來自iOS 6.0,說明問題只發生在iOS 6.0上。
(3) 異常
在這部分,你可以看到閃退發生時拋出的異常類型。還能看到異常編碼和拋出異常的線程。根據崩潰報告類型的不同,在這部分你還能看到一些另外的信息。
(4) 線程回溯
這部分提供應用中所有線程的回溯日志。 回溯是閃退發生時所有活動幀清單。它包含閃退發生時調用函數的清單。看下面這行日志:
它包括四列:
幀編號—— 此處是2。
二進制庫的名稱 ——此處是 XYZLib.
調用方法的地址 ——此處是 0x34648e88.
第四列分為兩個子列,一個基本地址和一個偏移量。此處是0×83000 + 8740, 第一個數字指向文件,第二個數字指向文件中的代碼行。
(5) 線程狀態
這部分是閃退時寄存器中的值。一般不需要這部分的信息,因為回溯部分的信息已經足夠讓你找出問題所在。
(6) 二進制映像
這部分列出了閃退時已經加載的二進制文件。
4. 符號化Symbolication
第一次看到崩潰日志上的回溯時,你或許會覺得它沒什麼意義。我們習慣使用方法名和行數,而非像這樣的神秘位置:
將這些十六進制地址轉化成方法名稱和行數的過程稱之為符號化。
從Xcode的Organizer窗口獲取崩潰日志後過幾秒鐘,崩潰日志將被自動符號化。上面那行被符號化後的版本如下 :
Xcode符號化崩潰日志時,需要訪問與App Store上對應的應用二進制文件以及生成二進制文件時產生的 .dSYM 文件。必需完全匹配才行。否則,日志將無法被完全符號化。
所以,保留每個分發給用戶的編譯版本非常重要。提交應用前進行歸檔時,Xcode將保存應用的二進制文件。可以在Xcode Organizer的Archives標簽欄下找到所有已歸檔的應用文件。
在發現崩潰日志時,如果有相匹配的.dSYM文件和應用二進制文件,Xcode會自動對崩潰日志進行符號化。如果你換到別的電腦或創建新的賬戶,務必將所有二進制文件移動到正確的位置,使Xcode能找到它們。
(注意:你必需同時保留應用二進制文件和.dSYM文件才能將崩潰日志完整符號化。每次提交到iTunes Connect的構建都必需歸檔。
.dSYM文件和二進制文件是特定綁定於每一次構建和後續構建的,即使來自相同的源代碼文件,每一次構建也與其他構建不同,不能相互替換。
如果你使用Build 和 Archive 命令,這些文件會自動放在適當位置。 如果不是使用Build 和 Archive命令,放在Spotlight能夠搜索到的位置(比如Home目錄)即可。)
5. 低內存閃退
因為低內存崩潰日志與普通崩潰日志略有不同,所以本教程特別分開說明一下。
iOS設備檢測到低內存時,虛擬內存系統發出通知請求應用釋放內存。這些通知發送到所有正在運行的應用和進程,試圖收回一些內存。
如果內存使用依然居高不下,系統將會終止後台線程以緩解內存壓力。如果可用內存足夠,應用將能夠繼續運行而不會產生崩潰報告。否則,應用將被iOS終止,並產生低內存崩潰報告。
低內存崩潰日志上沒有應用線程的堆棧回溯。相反,上面顯示的是以內存頁數為單位的各進程內存使用量。(在撰寫本文的時候,一個內存頁的大小是4KB。)
被iOS因釋放內存頁終止的進程名稱後面你會看到jettisoned字樣。如果看到它出現在你的應用名稱後面,說明你的應用因使用太多內存而被終止了。
6. 異常編碼
在研究真實閃退場景之前,還有一點需要重點介紹一下:就是那些有趣的異常編碼。你可以在報告的異常部分——前面代碼的第3部分找到異常編碼。有些編碼比較常見。
通常,異常編碼以一些文字開頭,緊接著是一個或多個十六進制值,此數值正是說明閃退根本性質的所在。 從這些編碼中,可以區分出閃退是因為程序錯誤、非法內存訪問或者是其他原因。
下面是一些常見的異常編碼:
0x8badf00d: 讀做 “ate bad food”! (把數字換成字母,是不是很像 :p)該編碼表示應用是因為發生watchdog超時而被iOS終止的。 通常是應用花費太多時間而無法啟動、終止或響應用系統事件。
0xbad22222:該編碼表示 VoIP應用因為過於頻繁重啟而被終止。
0xdead10cc:讀做 “deadlock”!該代碼表明應用因為在後台運行時占用系統資源,如通訊錄數據庫不釋放而被終止 。
0xdeadfa11:讀做 “dead fall”!該代碼表示應用是被用戶強制退出的。根據蘋果文檔, 強制退出發生在用戶長按開關按鈕直到出現 “滑動來關機”, 然後長按 Home按鈕。強制退出將產生 包含0xdeadfa11 異常編碼的崩潰日志, 因為大多數是強制退出是因為應用阻塞了界面。
SIGABRT: SIGABRT異常是由於某個對象接收到未實現的消息引起的