原文地址:http://www.raywenderlich.com/zh-hans/30818/ios%E5%BA%94%E7%94%A8%E5%B4%A9%E6%BA%83%E6%97%A5%E5%BF%97%E6%8F%AD%E7%A7%98
點擊開啟連結
這篇文章還可以在這裡找到 英語
If you're new here, you may want to subscribe to my RSS feed or follow me on Twitter. Thanks for visiting!
Learn how to make sense of crash logs!
本文作者是 Soheil Moayedi Azarpour, 他是一名獨立iOS開發人員。
作為一名應用開發人員,你是否有過如下經曆?
為確保你的應用正確無誤,在將其提交到市集之前,你必定進行了大量的測試工作。它在你的裝置上也運行得很好,但是,上了市集後,還是有使用者抱怨會閃退 !
如果你跟我一樣是個完美主義者,你肯定想將應用做到盡善盡美。於是你開啟代碼準備修複閃退的問題……但是,從何處著手呢。
這時iOS崩潰日誌派上用場了。在大多數情況下,你能從中瞭解到關於閃退的詳盡、有用的資訊。
通過本教程,你將學習到一些常見的崩潰日誌案例,以及如何從開發裝置和iTunes Connect上擷取崩潰記錄檔。你還將學習到符號化( symbolication),從日誌追蹤到代碼 。你還將學習調試一個在待定情況下會閃退的應用。
讓我們開始動手吧! 什麼是崩潰日誌,從哪裡能得它?
iOS裝置上的應用閃退時,作業系統會產生一個崩潰報告,也叫崩潰日誌,儲存在裝置上。
崩潰日誌上有很多有用的資訊,包括應用是什麼情況下閃退的。通常,上面有每個正在執行線程的完整堆疊追蹤資訊,所以你能從中瞭解到閃退發生時各線程都在做什麼,並分辨出閃退發生在哪個線程上。
有幾種方法可以從裝置上擷取崩潰日誌。
裝置與電腦上的iTunes Store同步後,會將崩潰日誌儲存在電腦上。根據電腦作業系統的不同,崩潰日誌將儲存在以下位置:
Mac OS X:
~/Library/Logs/CrashReporter/MobileDevice/
Windows XP:
C:Documents and Settings<USERNAME>Application DataApple ComputerLogsCrashReporterMobileDevice<DEVICE_NAME> |
Windows Vista or 7:
C:Users<USERNAME>AppDataRoamingApple ComputerLogsCrashReporterMobileDevice<DEVICE_NAME>
當使用者抱怨閃退時,你可以要求他讓裝置與iTunes同步,並根據作業系統的不同,到上述位置把崩潰日誌下載下來,然後通過電子郵件發送給你。
你必需盡量擷取使用者裝置產生的所有崩潰日誌。因為崩潰日誌越多,就越容易診斷問題所在!
另外,如果你裝了Xcode,也能很容易通過Xcode從你的裝置上獲得崩潰日誌。將iOS裝置串連到電腦上,然後開啟Xcode。從功能表列上選擇 Window 菜單, 然後選擇 Organizer (捷徑是 Shift-CMD-2).
在 Organizer 視窗上, 選中 Devices 標籤欄. 在左側的導航面板上,選中 Device Logs, 如下圖所示:
看看上圖,左側有好幾個 Device Logs 功能表項目. LIBRARY 下面的Device Logs是你所有裝置(曾經串連到Xcode的)的日誌 。每個裝置下面的 Device Logs 是對應裝置的日誌。
應用提交到App Store後,你也能從 iTunes Connect 擷取到使用者的崩潰日誌. 登入到 iTunes Connect 上, 選擇 Manage Your Applications, 點擊相應的應用, 點擊應用表徵圖下面的 View Details 按鈕, 然後點擊右欄Links部分的 Crash Reports 。
如果沒有崩潰日誌,試試驗擊Refresh 按鈕重新整理一下。如果你的應用還賣得不多,或者剛上架不久,iTunes Connect帳號上也可能還沒有任何崩潰日誌。
如果iTunes Connect上有崩潰日誌,你將看到如下圖:
有時,儘管有使用者報告閃退,你仍然看不到崩潰報告。這時,最好讓使用者直接把崩潰報告發送給你。 什麼情況下會產生崩潰日誌?
兩種主要情況會產生崩潰日誌: 應用違反作業系統規則。 應用中有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的應用,學習如何找到問題所在並進行修複! 崩潰日誌的執行個體
讓我們看看一個崩潰日誌的執行個體,以使你在處理一些實際問題之前心裡有譜。
事不宜遲,見見你的新朋友吧:
// 1: 進程資訊Incident Identifier: 30E46451-53FD-4965-896A-457FC11AD05FCrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31Hardware Model: iPhone4,1Process: Rage Masters [4155]Path: /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage MastersIdentifier: Rage MastersVersion: ??? (???)Code Type: ARM (Native)Parent Process: launchd [1]// 2: 基本資料Date/Time: 2012-10-17 21:39:06.967 -0400OS Version: iOS 6.0 (10A403)Report Version: 104// 3: 異常Exception Type: 00000020Exception Codes: 0x000000008badf00dHighlighted Thread: 0// 4: 線程回溯Thread 0 name: Dispatch queue: com.apple.main-threadThread 0:0 libsystem_kernel.dylib 0x327f2eb4 mach_msg_trap + 201 libsystem_kernel.dylib 0x327f3048 mach_msg + 362 CoreFoundation 0x36bd4040 __CFRunLoopServiceMachPort + 1243 CoreFoundation 0x36bd2d9e __CFRunLoopRun + 8784 CoreFoundation 0x36b45eb8 CFRunLoopRunSpecific + 3525 CoreFoundation 0x36b45d44 CFRunLoopRunInMode + 1006 CFNetwork 0x32ac343e CFURLConnectionSendSynchronousRequest + 3307 Foundation 0x346e69ba +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 2428 Rage Masters 0x000d4046 0xd2000 + 8262Thread 1:0 libsystem_kernel.dylib 0x32803d98 __workq_kernreturn + 81 libsystem_c.dylib 0x3a987cf6 _pthread_workq_return + 142 libsystem_c.dylib 0x3a987a12 _pthread_wqthread + 3623 libsystem_c.dylib 0x3a9878a0 start_wqthread + 4// 5: 線程狀態Thread 0 crashed with ARM Thread State (32-bit): r0: 0x00000000 r1: 0x00000000 r2: 0x00000001 r3: 0x39529fc8 r4: 0xffffffff r5: 0x2fd7d301 r6: 0x2fd7d300 r7: 0x2fd7d9d0 r8: 0x2fd7d330 r9: 0x3adbf8a8 r10: 0x2fd7d308 r11: 0x00000032 ip: 0x00000025 sp: 0x2fd7d2ec lr: 0x001bdb25 pc: 0x30301838 cpsr: 0x00000010// 6: 二進位映像Binary Images:0xd2000 - 0xd7fff +Rage Masters armv7 /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage Masters0x2fe41000 - 0x2fe61fff dyld armv7 /usr/lib/dyld0x327f2000 - 0x32808fff libsystem_kernel.dylib armv7 /usr/lib/system/libsystem_kernel.dylib0x328a8000 - 0x328bdfff libresolv.9.dylib armv7 /usr/lib/libresolv.9.dylib0x32a70000 - 0x32b35fff CFNetwork armv7 /System/Library/Frameworks/CFNetwork.framework/CFNetwork0x32b7a000 - 0x32cc3fff libicucore.A.dylib armv7 /usr/lib/libicucore.A.dylib0x32cc4000 - 0x32cc5fff CoreSurface armv7 /System/Library/PrivateFrameworks/CoreSurface.framework/CoreSurface0x32f65000 - 0x32f8afff OpenCL armv7 /System/Library/PrivateFrameworks/OpenCL.framework/OpenCL
這報告看起來像天書。:) 我們分幾部分來解讀吧:
(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 0x83000 + 8740
它包括四列: 幀編號—— 此處是2。 二進位庫的名稱 ——此處是 XYZLib. 調用方法的地址 ——此處是 0x34648e88. 第四列分為兩個子列,一個基本地址和一個位移量。此處是0×83000 + 8740, 第一個數字指向檔案,第二個數字指向檔案中的程式碼。
(5) 線程狀態
這部分是閃退時寄存器中的值。一般不需要這部分的資訊,因為回溯部分的資訊已經足夠讓你找出問題所在。
(6) 二進位映像
這部分列出了閃退時已經載入的二進位檔案。 符號化Symbolication
第一次看到崩潰日誌上的回溯時,你或許會覺得它沒什麼意義。我們習慣使用方法名和行數,而非像這樣的神秘位置:
6 Rage Masters 0x0001625c 0x2a000 + 3003 |
將這些十六進位地址轉化成方法名稱和行數的過程稱之為符號化。
從Xcode的Organizer視窗擷取崩潰日誌後過幾秒鐘,崩潰日誌將被自動符號化。上面那行被符號化後的版本如下 :