iOS应用崩溃日志分析 iOS应用崩溃日志揭秘
轉(zhuǎn)自: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
這篇文章還能夠在這里找到?英語
Learn how to make sense of crash logs!
本文作者是?Soheil Moayedi Azarpour, 他是一名獨(dú)立iOS開發(fā)人員。
作為一名應(yīng)用開發(fā)人員,你是否有過例如以下經(jīng)歷?
為確保你的應(yīng)用正確無誤,在將其提交到應(yīng)用商店之前,你必然進(jìn)行了大量的測(cè)試工作。它在你的設(shè)備上也執(zhí)行得非常好,可是,上了應(yīng)用商店后。還是實(shí)用戶抱怨會(huì)閃退 !
假設(shè)你跟我一樣是個(gè)完美主義者,你肯定想將應(yīng)用做到盡善盡美。于是你打開代碼準(zhǔn)備修復(fù)閃退的問題……可是,從何處著手呢?
這時(shí)iOS崩潰日志派上用場(chǎng)了。在大多數(shù)情況下。你能從中了解到關(guān)于閃退的詳盡、實(shí)用的信息。
通過本教程。你將學(xué)習(xí)到一些常見的崩潰日志案例,以及怎樣從開發(fā)設(shè)備和iTunes Connect上獲取崩潰日志文件。你還將學(xué)習(xí)到符號(hào)化( symbolication),從日志追蹤到代碼 。
你還將學(xué)習(xí)調(diào)試一個(gè)在待定情況下會(huì)閃退的應(yīng)用。
讓我們開始動(dòng)手吧!
什么是崩潰日志,從哪里能得它?
iOS設(shè)備上的應(yīng)用閃退時(shí)。操作系統(tǒng)會(huì)生成一個(gè)崩潰報(bào)告。也叫崩潰日志。保存在設(shè)備上。
崩潰日志上有非常多實(shí)用的信息,包含應(yīng)用是什么情況下閃退的。
通常。上面有每一個(gè)正在運(yùn)行線程的完整堆棧跟蹤信息,所以你能從中了解到閃退發(fā)生時(shí)各線程都在做什么,并分辨出閃退發(fā)生在哪個(gè)線程上。
有幾種方法能夠從設(shè)備上獲取崩潰日志。
設(shè)備與電腦上的iTunes Store同步后,會(huì)將崩潰日志保存在電腦上。依據(jù)電腦操作系統(tǒng)的不同。崩潰日志將保存在下面位置:
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>當(dāng)用戶抱怨閃退時(shí),你能夠要求他讓設(shè)備與iTunes同步。并依據(jù)操作系統(tǒng)的不同,到上述位置把崩潰日志下載下來。然后通過電子郵件發(fā)送給你。
你必需盡量獲取用戶設(shè)備生成的全部崩潰日志。由于崩潰日志越多,就越easy診斷問題所在!
另外。假設(shè)你裝了Xcode,也能非常easy通過Xcode從你的設(shè)備上獲得崩潰日志。將iOS設(shè)備連接到電腦上,然后打開Xcode。
從菜單條上選擇?Window ?菜單, 然后選擇?Organizer?(快捷方式是 Shift-CMD-2).
在 Organizer 窗體上, 選中?Devices?標(biāo)簽欄. 在左側(cè)的導(dǎo)航面板上。選中?Device Logs, 例如以下圖所看到的:
看看上圖。左側(cè)有好幾個(gè) Device Logs 菜單項(xiàng).?LIBRARY?以下的Device Logs是你全部設(shè)備(以前連接到Xcode的)的日志 。
每一個(gè)設(shè)備以下的 Device Logs 是相應(yīng)設(shè)備的日志。
應(yīng)用提交到App Store后,你也能從?iTunes Connect?獲取到用戶的崩潰日志. 登錄到 iTunes Connect 上, 選擇?Manage Your Applications, 點(diǎn)擊對(duì)應(yīng)的應(yīng)用, 點(diǎn)擊應(yīng)用圖標(biāo)以下的?View Details?button, 然后點(diǎn)擊右欄Links部分的 ?Crash Reports?。
假設(shè)沒有崩潰日志。試試點(diǎn)擊Refresh?button刷新一下。
假設(shè)你的應(yīng)用還賣得不多。或者剛上架不久。iTunes Connect賬號(hào)上也可能還沒有不論什么崩潰日志。
假設(shè)iTunes Connect上有崩潰日志,你將看到例如以下圖:
有時(shí)。雖然實(shí)用戶報(bào)告閃退,你仍然看不到崩潰報(bào)告。這時(shí),最好讓用戶直接把崩潰報(bào)告發(fā)送給你。
什么情況下會(huì)產(chǎn)生崩潰日志?
兩種主要情況會(huì)產(chǎn)生崩潰日志:
違反iOS規(guī)則包含在啟動(dòng)、恢復(fù)、掛起、退出時(shí)watchdog超時(shí)、用戶強(qiáng)制退出和低內(nèi)存終止。
讓我們具體了解一下吧。
Watchdog 超時(shí)機(jī)制
從iOS 4.x開始,退出應(yīng)用時(shí)。應(yīng)用不會(huì)馬上終止,而是退到后臺(tái)。
可是,假設(shè)你的應(yīng)用響應(yīng)不夠快,操作系統(tǒng)有可能會(huì)終止你的應(yīng)用,并產(chǎn)生一個(gè)崩潰日志。
這些事件與下列UIApplicationDelegate方法相相應(yīng):
- application:didFinishLaunchingWithOptions:
- applicationWillResignActive:
- applicationDidEnterBackground:
- applicationWillEnterForeground:
- applicationDidBecomeActive:
- applicationWillTerminate:
上面全部這些方法。應(yīng)用僅僅有有限的時(shí)間去完畢處理。假設(shè)花費(fèi)時(shí)間太長,操作系統(tǒng)將終止應(yīng)用。
注意:?假設(shè)你沒有把須要花費(fèi)時(shí)間比較長的操作(如網(wǎng)絡(luò)訪問)放在后臺(tái)線程上就非常easy發(fā)生這樣的情況。關(guān)于假設(shè)避免這樣的情況的信息,請(qǐng)參考我們的另外兩篇教程:?Grand Central Dispatch?和?NSOperations。
用戶強(qiáng)制退出
iOS 4.x開始支持多任務(wù)。
假設(shè)應(yīng)用堵塞界面并停止響應(yīng), 用戶能夠通過在主屏幕上雙擊Homebutton來終止應(yīng)用。此時(shí),操作應(yīng)用將生成一個(gè)崩潰日志。
注意:?雙擊Homebutton后。你將看到執(zhí)行過的全部應(yīng)用。
那些應(yīng)用不一定是正在執(zhí)行,也不一定是被掛起。
通常,用戶點(diǎn)擊Homebutton時(shí)。應(yīng)用將在后臺(tái)保留約10分鐘,然后操作系統(tǒng)自己主動(dòng)將其終止。 所以雙擊Homebutton顯示的應(yīng)用列表僅僅是表明那是一系列過去打開過的應(yīng)用。刪除那些應(yīng)用的圖標(biāo)不會(huì)產(chǎn)生不論什么崩潰日志。
低內(nèi)存終止
子類化UIViewController時(shí),你也許已經(jīng)注意到didReceiveMemoryWarning方法。
在前臺(tái)執(zhí)行的應(yīng)用擁有訪問和使用內(nèi)存的最高優(yōu)化級(jí)。然而。這并不意味著該應(yīng)用能使用設(shè)備的全部可用內(nèi)存 ——每一個(gè)應(yīng)用僅僅能使用一部分可用內(nèi)存。
當(dāng)內(nèi)存使用達(dá)到一定程度時(shí),操作系統(tǒng)將發(fā)出一個(gè)?UIApplicationDidReceiveMemoryWarningNotification?通知。同一時(shí)候,調(diào)用?didReceiveMemoryWarning?方法。
此時(shí),為了讓應(yīng)用繼續(xù)正常執(zhí)行。操作系統(tǒng)開始終止在后臺(tái)的其它應(yīng)用以釋放一些內(nèi)存。全部后臺(tái)應(yīng)用被終止后,假設(shè)你的應(yīng)用還須要很多其它內(nèi)存,操作系統(tǒng)會(huì)將你的應(yīng)用也終止掉,并產(chǎn)生一個(gè)崩潰日志。而在這樣的情況下被終止的后臺(tái)應(yīng)用,不會(huì)產(chǎn)生崩潰日志。
注意:?依據(jù)?蘋果文檔, Xcode不會(huì)自己主動(dòng)加入低內(nèi)存日志。你必需手動(dòng)獲取日志。 然而,依據(jù)我的個(gè)人經(jīng)驗(yàn)。使用 Xcode 4.5.2, 低內(nèi)存日志也會(huì)自己主動(dòng)導(dǎo)入,僅僅是”Process”和”Type”屬性都被標(biāo)為Unknown(未知)。
另外,值得一提的是在極短時(shí)間內(nèi)分配一大塊內(nèi)存將給系統(tǒng)內(nèi)存帶來巨大負(fù)擔(dān)。
這樣。也會(huì)產(chǎn)生內(nèi)存警告的通知。
應(yīng)用中有Bug
如你所想。大多數(shù)閃退都是由于應(yīng)用中有Bug,因此大多數(shù)崩潰日志的產(chǎn)生都是由于應(yīng)用中的Bug。Bug的種類的有非常多。
在本教程的后半部分,你將通過調(diào)試一個(gè)會(huì)產(chǎn)生崩潰日志的含有Bug的應(yīng)用,學(xué)習(xí)怎樣找到問題所在并進(jìn)行修復(fù)!
崩潰日志的實(shí)例
讓我們看看一個(gè)崩潰日志的實(shí)例,以使你在處理一些實(shí)際問題之前心里有譜。
事不宜遲,見見你的新朋友吧:
| // 1: 進(jìn)程信息 Incident Identifier: 30E46451-53FD-4965-896A-457FC11AD05F CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31 Hardware Model: iPhone4,1 Process: Rage Masters [4155] Path: /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage Masters Identifier: Rage Masters Version: ??? (???) Code Type: ARM (Native) Parent Process: launchd [1] // 2: 基本信息 Date/Time: 2012-10-17 21:39:06.967 -0400 OS Version: iOS 6.0 (10A403) Report Version: 104 // 3: 異常 Exception Type: 00000020 Exception Codes: 0x000000008badf00d Highlighted Thread: 0 // 4: 線程回溯 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0: 0 libsystem_kernel.dylib 0x327f2eb4 mach_msg_trap + 20 1 libsystem_kernel.dylib 0x327f3048 mach_msg + 36 2 CoreFoundation 0x36bd4040 __CFRunLoopServiceMachPort + 124 3 CoreFoundation 0x36bd2d9e __CFRunLoopRun + 878 4 CoreFoundation 0x36b45eb8 CFRunLoopRunSpecific + 352 5 CoreFoundation 0x36b45d44 CFRunLoopRunInMode + 100 6 CFNetwork 0x32ac343e CFURLConnectionSendSynchronousRequest + 330 7 Foundation 0x346e69ba +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 242 8 Rage Masters 0x000d4046 0xd2000 + 8262 Thread 1: 0 libsystem_kernel.dylib 0x32803d98 __workq_kernreturn + 8 1 libsystem_c.dylib 0x3a987cf6 _pthread_workq_return + 14 2 libsystem_c.dylib 0x3a987a12 _pthread_wqthread + 362 3 libsystem_c.dylib 0x3a9878a0 start_wqthread + 4 // 5: 線程狀態(tài) Thread 0 crashed with ARM Thread State (32-bit):r0: 0x00000000 r1: 0x00000000 r2: 0x00000001 r3: 0x39529fc8r4: 0xffffffff r5: 0x2fd7d301 r6: 0x2fd7d300 r7: 0x2fd7d9d0r8: 0x2fd7d330 r9: 0x3adbf8a8 r10: 0x2fd7d308 r11: 0x00000032ip: 0x00000025 sp: 0x2fd7d2ec lr: 0x001bdb25 pc: 0x30301838cpsr: 0x00000010 // 6: 二進(jìn)制映像 Binary Images: 0xd2000 - 0xd7fff +Rage Masters armv7 /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage Masters 0x2fe41000 - 0x2fe61fff dyld armv7 /usr/lib/dyld 0x327f2000 - 0x32808fff libsystem_kernel.dylib armv7 /usr/lib/system/libsystem_kernel.dylib 0x328a8000 - 0x328bdfff libresolv.9.dylib armv7 /usr/lib/libresolv.9.dylib 0x32a70000 - 0x32b35fff CFNetwork armv7 /System/Library/Frameworks/CFNetwork.framework/CFNetwork 0x32b7a000 - 0x32cc3fff libicucore.A.dylib armv7 /usr/lib/libicucore.A.dylib 0x32cc4000 - 0x32cc5fff CoreSurface armv7 /System/Library/PrivateFrameworks/CoreSurface.framework/CoreSurface 0x32f65000 - 0x32f8afff OpenCL armv7 /System/Library/PrivateFrameworks/OpenCL.framework/OpenCL |
這報(bào)告看起來像天書。
:) 我們分幾部分來解讀吧:
(1) 進(jìn)程信息
第一部分是閃退進(jìn)程的相關(guān)信息。
- Incident Identifier是崩潰報(bào)告的唯一標(biāo)識(shí)符。
- CrashReporter Key?是與設(shè)備標(biāo)識(shí)相相應(yīng)的唯一鍵值。盡管它不是真正的設(shè)備標(biāo)識(shí)符,但也是一個(gè)很實(shí)用的情報(bào):假設(shè)你看到100個(gè)崩潰日志的CrashReporter Key值都是同樣的。或者僅僅有少數(shù)幾個(gè)不同的CrashReport值,說明這不是一個(gè)普遍的問題,僅僅發(fā)生在一個(gè)或少數(shù)幾個(gè)設(shè)備上。
- Hardware Model?標(biāo)識(shí)設(shè)備類型。 假設(shè)非常多崩潰日志都是來自同樣的設(shè)備類型。說明應(yīng)用僅僅在某特定類型的設(shè)備上有問題。上面的日志里。崩潰日志產(chǎn)生的設(shè)備是iPhone 4s。
- Process?是應(yīng)用名稱。中括號(hào)中面的數(shù)字是閃退時(shí)應(yīng)用的進(jìn)程ID。
- 接下來幾行不言自明,無需贅述。
(2) 基本信息
這部分給出了一些基本信息,包含閃退發(fā)生的日期和時(shí)間,設(shè)備的iOS版本號(hào)。假設(shè)有非常多崩潰日志都來自iOS 6.0。說明問題僅僅發(fā)生在iOS 6.0上。
(3) 異常
在這部分。你能夠看到閃退發(fā)生時(shí)拋出的異常類型。
還能看到異常編碼和拋出異常的線程。依據(jù)崩潰報(bào)告類型的不同。在這部分你還能看到一些另外的信息。
(4) 線程回溯
這部分提供應(yīng)用中全部線程的回溯日志。?回溯是閃退發(fā)生時(shí)全部活動(dòng)幀清單。它包括閃退發(fā)生時(shí)調(diào)用函數(shù)的清單。
看以下這行日志:
| 2 XYZLib 0x34648e88 0x83000 + 8740 |
它包含四列:
(5) 線程狀態(tài)
這部分是閃退時(shí)寄存器中的值。
一般不須要這部分的信息,由于回溯部分的信息已經(jīng)足夠讓你找出問題所在。
(6) 二進(jìn)制映像
這部分列出了閃退時(shí)已經(jīng)載入的二進(jìn)制文件。
符號(hào)化Symbolication
第一次看到崩潰日志上的回溯時(shí)。你也許會(huì)認(rèn)為它沒什么意義。我們習(xí)慣用法名和行數(shù),而非像這種神奇位置:
| 6 Rage Masters 0x0001625c 0x2a000 + 3003 |
將這些十六進(jìn)制地址轉(zhuǎn)化成方法名稱和行數(shù)的過程稱之為符號(hào)化。
從Xcode的Organizer窗體獲取崩潰日志后過幾秒鐘。崩潰日志將被自己主動(dòng)符號(hào)化。上面那行被符號(hào)化后的版本號(hào)例如以下 :
| 6 Rage Masters 0x0001625c -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:35) |
Xcode符號(hào)化崩潰日志時(shí),須要訪問與App Store上相應(yīng)的應(yīng)用二進(jìn)制文件以及生成二進(jìn)制文件時(shí)產(chǎn)生的?.dSYM?文件。必需全然匹配才行。否則,日志將無法被全然符號(hào)化。
所以,保留每一個(gè)分發(fā)給用戶的編譯版本號(hào)很重要。
提交應(yīng)用前進(jìn)行歸檔時(shí),Xcode將保存應(yīng)用的二進(jìn)制文件。能夠在Xcode Organizer的Archives標(biāo)簽欄下找到全部已歸檔的應(yīng)用文件。
在發(fā)現(xiàn)崩潰日志時(shí),假設(shè)有相匹配的.dSYM文件和應(yīng)用二進(jìn)制文件。Xcode會(huì)自己主動(dòng)對(duì)崩潰日志進(jìn)行符號(hào)化。假設(shè)你換到別的電腦或創(chuàng)建新的賬戶。務(wù)必將全部二進(jìn)制文件移動(dòng)到正確的位置,使Xcode能找到它們。
注意:?你必需同一時(shí)候保留應(yīng)用二進(jìn)制文件和.dSYM文件才干將崩潰日志完整符號(hào)化。每次提交到iTunes Connect的構(gòu)建都必需歸檔。
.dSYM文件和二進(jìn)制文件是特定綁定于每一次構(gòu)建和興許構(gòu)建的,即使來自同樣的源碼文件。每一次構(gòu)建也與其它構(gòu)建不同。不能相互替換。
假設(shè)你使用Build 和 Archive 命令,這些文件會(huì)自己主動(dòng)放在適當(dāng)位置。 假設(shè)不是使用Build 和 Archive命令,放在Spotlight可以搜索到的位置(比方Home文件夾)就可以。
?
低內(nèi)存閃退
由于低內(nèi)存崩潰日志與普通崩潰日志略有不同,所以本教程特別分開說明一下。
:]
iOS設(shè)備檢測(cè)到低內(nèi)存時(shí),虛擬內(nèi)存系統(tǒng)發(fā)出通知請(qǐng)求應(yīng)用釋放內(nèi)存。這些通知發(fā)送到全部正在執(zhí)行的應(yīng)用和進(jìn)程,試圖收回一些內(nèi)存。
假設(shè)內(nèi)存使用依舊居高不下。系統(tǒng)將會(huì)終止后臺(tái)線程以緩解內(nèi)存壓力。
假設(shè)可用內(nèi)存足夠,應(yīng)用將可以繼續(xù)執(zhí)行而不會(huì)產(chǎn)生崩潰報(bào)告。否則,應(yīng)用將被iOS終止。并產(chǎn)生低內(nèi)存崩潰報(bào)告。
低內(nèi)存崩潰日志上沒有應(yīng)用線程的堆棧回溯。相反,上面顯示的是以內(nèi)存頁數(shù)為單位的各進(jìn)程內(nèi)存使用量。
(在撰寫本文的時(shí)候,一個(gè)內(nèi)存頁的大小是4KB。)
被iOS因釋放內(nèi)存頁終止的進(jìn)程名稱后面你會(huì)看到jettisoned?字樣。假設(shè)看到它出如今你的應(yīng)用名稱后面,說明你的應(yīng)用因使用太多內(nèi)存而被終止了。
低內(nèi)存崩潰日志看起來像這樣:
| Incident Identifier: 30E46451-53FD-4965-896A-457FC11AD05F CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31 OS Version: iPhone OS 3.1.3 (7E18) Date/Time: 2012-10-17 21:39:06.967 -0400 Free pages: 96 Wired pages: 10558 Purgeable pages: 0 Largest process: Rage Masters ProcessesName UUID Count resident pagesRage Masters 9320 (jettisoned) (active)mediaserverd 255dataaccessd 505syslogd 71apsd 171securityd 243notifyd 2027CommCenter 189SpringBoard 2158 (active)accessoryd 91configd 371fairplayd 93mDNSResponder 292lockdownd 1204launchd 72 |
當(dāng)應(yīng)用發(fā)生低內(nèi)存閃退時(shí),你必需看看應(yīng)用中內(nèi)存使用的方式,以及是怎樣處理低內(nèi)存警告的。
你能夠使用Instruments工具中使用Allocations 和 Leaks來發(fā)現(xiàn)內(nèi)存分配問題和內(nèi)存泄漏問題。
假設(shè)你不知道怎樣利用 Instruments 檢查內(nèi)存問題,能夠看看這個(gè)教程?。
還有,別忘記虛擬內(nèi)存!
Instruments工具的Leaks 和 Allocations 不能跟蹤顯存使用情況。必需使用 VM Tracker 才干查看顯存使用情況。
VM Tracker 默認(rèn)是關(guān)閉的。打開Instrument,手動(dòng) 選中Automatic Snapshotting?標(biāo)志或者按下Snapshot Now?button。
本教程后面將會(huì)學(xué)習(xí)怎樣研究低內(nèi)存崩潰日志。
異常編碼
在研究真實(shí)閃退場(chǎng)景之前,另一點(diǎn)須要重點(diǎn)介紹一下:就是那些有趣的異常編碼 。
你能夠在報(bào)告的異常部分——前面代碼的第3部分找到異常編碼。
有些編碼比較常見。
通常。異常編碼以一些文字開頭,緊接著是一個(gè)或多個(gè)十六進(jìn)制值。此數(shù)值正是說明閃退根本性質(zhì)的所在。 ?從這些編碼中,能夠區(qū)分出閃退是由于程序錯(cuò)誤、非法內(nèi)存訪問或者是其它原因。
以下是一些常見的異常編碼:
- 0x8badf00d: 讀做 “ate bad food”! (把數(shù)字換成字母。是不是非常像 :p)該編碼表示應(yīng)用是由于發(fā)生watchdog超時(shí)而被iOS終止的。
?一般是應(yīng)用花費(fèi)太多時(shí)間而無法啟動(dòng)、終止或響應(yīng)用系統(tǒng)事件。
- 0xbad22222: 該編碼表示 VoIP 應(yīng)用由于過于頻繁重新啟動(dòng)而被終止。
- 0xdead10cc: 讀做 “dead lock”!該代碼表明應(yīng)用由于在后臺(tái)執(zhí)行時(shí)占用系統(tǒng)資源。如通訊錄數(shù)據(jù)庫不釋放而被終止 。
- 0xdeadfa11: 讀做 “dead fall”! 該代碼表示應(yīng)用是被用戶強(qiáng)制退出的。
依據(jù)蘋果文檔, 強(qiáng)制退出發(fā)生在用戶長按開關(guān)button直到出現(xiàn) “滑動(dòng)來關(guān)機(jī)”, 然后長按 Homebutton。強(qiáng)制退出將產(chǎn)生 包括0xdeadfa11 異常編碼的崩潰日志, 由于大多數(shù)是強(qiáng)制退出是由于應(yīng)用堵塞了界面。
?
注意:?在后臺(tái)任務(wù)列表中關(guān)閉已掛起的應(yīng)用不會(huì)產(chǎn)生崩潰日志。
一旦應(yīng)用被掛起,它何時(shí)被終止都是合理的。所以不會(huì)產(chǎn)生崩潰日志。
大展身手的時(shí)候到了!
好了! 你已經(jīng)學(xué)習(xí)了全部分析崩潰日志和修復(fù)錯(cuò)誤的基礎(chǔ)知識(shí)!
如果你剛進(jìn)入Rage-O-Rage有限公司工作。
該公司有一個(gè)在App Store上熱銷的應(yīng)用,叫 Rage Masters。
你的老板安迪要你幫忙解決幾個(gè)用戶常常抱怨閃退問題。你的任務(wù)就是研究這些閃退,符號(hào)化用戶提供的崩潰日志,查找問題所在,并修復(fù)之。
你能夠從?這里下載應(yīng)用的源碼。
注意: 假設(shè)你想自己又一次生成崩潰報(bào)告,請(qǐng)遵照下面指引:
?
場(chǎng)景 1: 糟糕的代碼
一封來自用戶的郵件: “大哥,你的應(yīng)用就是一坨屎! 我將其下載到我自己的iPod Touch和iPhone上。還下載到我兒子的iPod Touch上。在全部的設(shè)備上,都是還沒打開就閃退了……”
別一封來自用戶的郵件說, “我下載了你們的應(yīng)用,一打開就閃退。
真悲催…”
還有一封郵件說得更明白:”你們的應(yīng)用不能執(zhí)行。我把它下載到我和妻子的設(shè)備上。全部設(shè)備都是 一打開就閃退了…”
好吧,別灰心! 這些意見藏著什么玄機(jī)呢?讓我們看看崩潰日志吧:
| Incident Identifier: 85833DBA-3DF7-43EE-AF80-4E5C51091F42
CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model: iPhone4,1
Process: Rage Masters [20067]
Path: /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier: Rage Masters
Version: ?? ? (? ??) Code Type: ARM (Native) Parent Process: launchd [1] Date/Time: 2012-11-03 13:37:31.148 -0400 OS Version: iOS 6.0 (10A403) Report Version: 104 Exception Type: 00000020 Exception Codes: 0x000000008badf00d Highlighted Thread: 0 Application Specific Information: Soheil-Azarpour.Rage-Masters failed to launch in time Elapsed total CPU time (seconds): 8.030 (user 8.030, system 0.000), 20% CPU Elapsed application CPU time (seconds): 3.840, 10% CPU Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0: 0 libsystem_kernel.dylib 0x327f2eb4 mach_msg_trap + 20 1 libsystem_kernel.dylib 0x327f3048 mach_msg + 36 2 CoreFoundation 0x36bd4040 __CFRunLoopServiceMachPort + 124 3 CoreFoundation 0x36bd2d9e __CFRunLoopRun + 878 4 CoreFoundation 0x36b45eb8 CFRunLoopRunSpecific + 352 5 CoreFoundation 0x36b45d44 CFRunLoopRunInMode + 100 6 CFNetwork 0x32ac343e CFURLConnectionSendSynchronousRequest + 330 7 Foundation 0x346e69ba +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 242 8 Rage Masters 0x000ea1c4 -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:36) 9 UIKit 0x37f30ad4 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 248 10 UIKit 0x37f3065e -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1186 11 UIKit 0x37f28846 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 694 12 UIKit 0x37ed0c3c -[UIApplication handleEvent:withNewEvent:] + 1000 13 UIKit 0x37ed06d0 -[UIApplication sendEvent:] + 68 14 UIKit 0x37ed011e _UIApplicationHandleEvent + 6150 15 GraphicsServices 0x370835a0 _PurpleEventCallback + 588 16 GraphicsServices 0x370831ce PurpleEventCallback + 30 17 CoreFoundation 0x36bd4170 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32 18 CoreFoundation 0x36bd4112 __CFRunLoopDoSource1 + 134 19 CoreFoundation 0x36bd2f94 __CFRunLoopRun + 1380 20 CoreFoundation 0x36b45eb8 CFRunLoopRunSpecific + 352 21 CoreFoundation 0x36b45d44 CFRunLoopRunInMode + 100 22 UIKit 0x37f27480 -[UIApplication _run] + 664 23 UIKit 0x37f242fc UIApplicationMain + 1116 24 Rage Masters 0x000ea004 main (main.m:16) 25 libdyld.dylib 0x3b630b1c start + 0 |
發(fā)現(xiàn)問題了嗎?
異常編碼是0x000000008badf00d,還有后面的報(bào)告:
| Application Specific Information: Soheil-Azarpour.Rage-Masters failed to launch in time Elapsed total CPU time (seconds): 8.030 (user 8.030, system 0.000), 20% CPU Elapsed application CPU time (seconds): 3.840, 10% CPU |
這說明應(yīng)用在啟動(dòng)時(shí)就閃退了。iOS的watchdog機(jī)制終止了應(yīng)用。帥! 找到問題了,可是為什會(huì)發(fā)生這種事呢?
接著往下看日志。 從下向上讀回溯日志。
最底下的幀 (frame 25: libdyld.dylib)是最先調(diào)用的,然后是幀24, Rage Masters, main (main.m:16) ,依此類推。
跟應(yīng)用源碼相關(guān)的幀是最重要的。忽略掉系統(tǒng)庫和框架。下一個(gè)與代碼相關(guān)的幀是:
| 8 Rage Masters 0x0009f244 -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:35) |
應(yīng)用在運(yùn)行RMAppDelegate (RMAppDelegate.m:35)類application:didFinishLaunchingWithOptions:?方法第35 行代碼時(shí)閃退。
打開Xcode看看那行代碼:
| NSData *directoryData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; |
就是它了! 同步調(diào)用web服務(wù)?! 在主線程上?
! 在?application:didFinishLaunchingWithOptions: 方法上?!! 誰寫的代碼呀?!
Network calls on the main thread makes kittens sad.
無論怎樣,問題得你來修復(fù)了。這個(gè)調(diào)用必需異步進(jìn)行,甚至更理想的情況是,在application:didFinishLaunchingWithOptions:返回YES之后的其它部分再運(yùn)行Web服務(wù)。
在其它地方調(diào)用可能須要比較多的改動(dòng)。
當(dāng)下,我們僅僅要使應(yīng)用不閃退即可。
能夠在日后再實(shí)現(xiàn)更好的設(shè)計(jì)。
將上面那行討厭的代碼(及其以下的三行代碼)換成以下這個(gè)異步的版本號(hào)吧:
| [NSURLConnection sendAsynchronousRequest:requestqueue:[NSOperationQueue mainQueue]completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){NSURL *cacheDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSUserDirectory inDomains:NSCachesDirectory] lastObject];NSURL *filePath = [NSURL URLWithString:kDirectoryFile relativeToURL:cacheDirectory];[data writeToFile:[filePath absoluteString] atomically:YES];}]; |
?
場(chǎng)景 2: 無法響應(yīng)事件的button
一名用戶說: “我不能將某個(gè)rage master加入到書簽里面。我想加入的時(shí)候應(yīng)用就閃退…”
用一名用戶說 :”書簽不能用 … 在具體頁面上,點(diǎn)擊書簽button,應(yīng)用就閃退了!”
上面的抱怨說得不是非常清楚,引起問題的解決辦法肯定有多樣。看看崩潰日志:
| Incident Identifier: 3AAA63CC-3088-41CC-84D9-82FE03F9F354
CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model: iPhone4,1
Process: Rage Masters [20090]
Path: /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier: Rage Masters
Version: ?? ? (???) Code Type: ARM (Native) Parent Process: launchd [1] Date/Time: 2012-11-03 13:39:00.081 -0400 OS Version: iOS 6.0 (10A403) Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Crashed Thread: 0 Last Exception Backtrace: 0 CoreFoundation 0x36bff29e __exceptionPreprocess + 158 1 libobjc.A.dylib 0x34f0f97a objc_exception_throw + 26 2 CoreFoundation 0x36c02e02 -[NSObject(NSObject) doesNotRecognizeSelector:] + 166 3 CoreFoundation 0x36c0152c ___forwarding___ + 388 4 CoreFoundation 0x36b58f64 _CF_forwarding_prep_0 + 20 5 UIKit 0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68 6 UIKit 0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26 7 UIKit 0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40 8 UIKit 0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498 9 UIKit 0x37fbade4 -[UIControl touchesEnded:withEvent:] + 484 10 UIKit 0x37ee35f4 -[UIWindow _sendTouchesForEvent:] + 520 11 UIKit 0x37ed0804 -[UIApplication sendEvent:] + 376 12 UIKit 0x37ed011e _UIApplicationHandleEvent + 6150 13 GraphicsServices 0x3708359e _PurpleEventCallback + 586 14 GraphicsServices 0x370831ce PurpleEventCallback + 30 15 CoreFoundation 0x36bd416e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 30 16 CoreFoundation 0x36bd4112 __CFRunLoopDoSource1 + 134 17 CoreFoundation 0x36bd2f94 __CFRunLoopRun + 1380 18 CoreFoundation 0x36b45eb8 CFRunLoopRunSpecific + 352 19 CoreFoundation 0x36b45d44 CFRunLoopRunInMode + 100 20 GraphicsServices 0x370822e6 GSEventRunModal + 70 21 UIKit 0x37f242fc UIApplicationMain + 1116 22 Rage Masters 0x000ca004 main (main.m:16) 23 libdyld.dylib 0x3b630b1c start + 0 |
異常代碼是SIGABRT。通常, ?SIGABRT 異常是因?yàn)槟硞€(gè)對(duì)象接收到未實(shí)現(xiàn)的消息引起的。 或者。用簡單的話說,在某個(gè)對(duì)象上調(diào)用了不存在的方法。
這樣的情況一般不會(huì)發(fā)生。由于A對(duì)象調(diào)用了B方法。假設(shè)B方法不存在,編譯器會(huì)報(bào)錯(cuò)。可是。假設(shè)你是使用selector間接調(diào)用方法的。編譯器則無法檢測(cè)對(duì)象是否存在該方法了。
回到崩潰日志。它指出閃退發(fā)生在編號(hào)為0的線程上。 這意味著非常可能是在主線程上調(diào)用了某個(gè)對(duì)象沒有實(shí)現(xiàn)的方法。
假設(shè)你接著閱讀回溯日志,會(huì)發(fā)現(xiàn)跟你的代碼相關(guān)的僅僅有幀22, main.m:16. 這沒有多大幫助。 :[
繼續(xù)向上查看框架調(diào)用。出現(xiàn)這個(gè):
| 2 CoreFoundation 0x36c02e02 -[NSObject(NSObject) doesNotRecognizeSelector:] + 166 |
這不是你自己寫的代碼。但至少它確認(rèn)了是對(duì)象調(diào)用了一個(gè)沒有實(shí)現(xiàn)的方法。
回到RMDetailViewController.m文件, 由于那是書簽button實(shí)現(xiàn)動(dòng)作的地方。 找到書簽功能代碼:
| -(IBAction)bookmarkButtonPressed {self.master.isBookmarked = !self.master.isBookmarked;// Update shared bookmarksif (self.master.isBookmarked)[[RMBookmarks sharedBookmarks] bookmarkMaster:self.master];else[[RMBookmarks sharedBookmarks] unbookmarkMaster:self.master];// Update UI[self updateBookmarkImage]; } |
看起來沒什么問題,再檢查一下storyboard (XIB文件) 。確認(rèn)button連接的正確性。
就是它了! 在?MainStoryboard.storyboard,button連接的是 bookmarkButtonPressed: 而不是bookmarkButtonPressed (注意后面的分號(hào)說明方法有一個(gè)參數(shù))。 僅僅要將上面的方法簽名改動(dòng)成這樣就能修復(fù)問題了:
| -(IBAction)bookmarkButtonPressed:(id)sender {// Remain unchanged.. } |
當(dāng)然,你也能夠簡單地在XIB文件上刪除錯(cuò)誤的連接,然后又一次連接方法,使XIB文件連接到正確的方法上。
兩者方法都行。
又處理了一個(gè)閃退問題。好樣的。:]
場(chǎng)景 3: 表格上的Bug
還有一用戶抱怨道, “在書簽視圖上無法刪除書簽…” 還有還有一用戶抱怨相同的問題, “當(dāng)我試圖刪除書簽時(shí),應(yīng)用閃退…”
這些郵件沒什么作用,還是看看崩潰日志!
| Incident Identifier: 5B62D681-D8FE-41FE-8D52-AB7E6D6B2AC7
CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model: iPhone4,1
Process: Rage Masters [20088]
Path: /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier: Rage Masters
Version: ? ?? (? ??) Code Type: ARM (Native) Parent Process: launchd [1] Date/Time: 2012-11-03 13:38:45.762 -0400 OS Version: iOS 6.0 (10A403) Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Crashed Thread: 0 Last Exception Backtrace: 0 CoreFoundation 0x36bff29e __exceptionPreprocess + 158 1 libobjc.A.dylib 0x34f0f97a objc_exception_throw + 26 2 CoreFoundation 0x36bff158 +[NSException raise:format:arguments:] + 96 3 Foundation 0x346812aa -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 86 4 UIKit 0x37f04b7e -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] + 7690 5 UIKit 0x3803a4a2 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] + 22 6 Rage Masters 0x000fd9ca -[RMBookmarksViewController tableView:commitEditingStyle:forRowAtIndexPath:] (RMBookmarksViewController.m:68) 7 UIKit 0x3809a5d4 -[UITableView(UITableViewInternal) animateDeletionOfRowWithCell:] + 80 8 UIKit 0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68 9 UIKit 0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26 10 UIKit 0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40 11 UIKit 0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498 12 UIKit 0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68 13 UIKit 0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26 14 UIKit 0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40 15 UIKit 0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498 16 UIKit 0x37fbade4 -[UIControl touchesEnded:withEvent:] + 484 17 UIKit 0x37ee35f4 -[UIWindow _sendTouchesForEvent:] + 520 18 UIKit 0x37ed0804 -[UIApplication sendEvent:] + 376 19 UIKit 0x37ed011e _UIApplicationHandleEvent + 6150 20 GraphicsServices 0x3708359e _PurpleEventCallback + 586 21 GraphicsServices 0x370831ce PurpleEventCallback + 30 22 CoreFoundation 0x36bd416e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 30 23 CoreFoundation 0x36bd4112 __CFRunLoopDoSource1 + 134 24 CoreFoundation 0x36bd2f94 __CFRunLoopRun + 1380 25 CoreFoundation 0x36b45eb8 CFRunLoopRunSpecific + 352 26 CoreFoundation 0x36b45d44 CFRunLoopRunInMode + 100 27 GraphicsServices 0x370822e6 GSEventRunModal + 70 28 UIKit 0x37f242fc UIApplicationMain + 1116 29 Rage Masters 0x000fb004 main (main.m:16) 30 libdyld.dylib 0x3b630b1c start + 0 |
這看起來跟前面那個(gè)崩潰日志非常像。是還有一個(gè)SIGABRT 異常。 你可能想知道是否是同樣的問題:發(fā)送信息到一個(gè)沒有實(shí)現(xiàn)對(duì)應(yīng)方法的對(duì)象?
讓我們從回溯日志看看哪些方法被調(diào)用了。從底部開始,你的源碼最后被調(diào)用的是幀 6:
| 6 Rage Masters 0x00088c66 -[RMBookmarksViewController tableView:commitEditingStyle:forRowAtIndexPath:] (RMBookmarksViewController.m:68) |
這是UITableViewDataSource 的一個(gè)方法. 呵呵?! 毫無疑問蘋果已經(jīng)實(shí)現(xiàn)了該方法 —— 你能夠重載它, 但不像是還沒有實(shí)現(xiàn)。并且,這是個(gè)可選的委派方法。 所以問題不是調(diào)用了一個(gè)沒有實(shí)現(xiàn)的方法。
再看看上面的幾個(gè)幀:
| 3 Foundation 0x346812aa -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 86 4 UIKit 0x37f04b7e -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] + 7690 5 UIKit 0x3803a4a2 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] + 22 |
幀 5, UITableView調(diào)用了它自己的還有一個(gè)方法?deleteRowsAtIndexPaths:withRowAnimation:?然后是看起來像蘋果內(nèi)部方法的_endCellAnimationsWithContext:?被調(diào)用。
然后Foundation framework發(fā)生異常handleFailureInMethod:object:file:lineNumber:description:.
這些分析結(jié)合用戶的抱怨,看起來是你在處理UITableView刪除行過程中有Bug。回到Xcode。你知道看哪里嗎 ? 能從崩潰日志中推斷出來?
就是RMBookmarksViewController.m文件的第68行:
| - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } |
發(fā)現(xiàn)問題了嗎? 給你點(diǎn)時(shí)間,細(xì)致看一下。
找到了吧! 數(shù)據(jù)源呢?
代碼在表格視圖上刪除了一行。但并沒有改動(dòng)背后的數(shù)據(jù)源。把上面的代碼替換成以下的就能修復(fù)問題了:
| -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {RMMaster *masterToDelete = [bookmarks objectAtIndex:indexPath.row];[bookmarks removeObject:masterToDelete];[[RMBookmarks sharedBookmarks] unbookmarkMaster:masterToDelete];[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } |
搞定了!走起,討厭的 bug!!
這篇文章還能夠在這里找到?英語
場(chǎng)景 4: 吃棒棒糖時(shí)閃退!
用戶郵件說, “當(dāng)rage master吃棒棒糖時(shí)應(yīng)用就閃退…” 還有一用戶說, “我讓rage master 吃棒棒糖。沒幾次應(yīng)用就閃退了!”
崩潰日志例如以下:
| Incident Identifier: 081E58F5-95A8-404D-947B-5E104B6BC1B1 CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31 Hardware Model: iPhone4,1 OS Version: iPhone OS 6.0 (10A403) Kernel Version: Darwin Kernel Version 13.0.0: Sun Aug 19 00:28:05 PDT 2012; root:xnu-2107.2.33~4/RELEASE_ARM_S5L8940X Date: 2012-11-03 13:39:59 -0400 Time since snapshot: 4353 ms Free pages: 968 Active pages: 7778 Inactive pages: 4005 Throttled pages: 92319 Purgeable pages: 0 Wired pages: 23347 Largest process: Rage Masters |
| ProcessesName <UUID> rpages recent_max [reason] (state)lsd <6a9f5b5f36b23fc78f87b6d8f1f49a9d> 331 331 [vm] (daemon) (idle)afcd <b0aff2e7952e34a9882fec81a8dcdbb2> 141 141 [vm] (daemon) (idle)itunesstored <4e0cd9f873de3435b4119c48b2d6d13d> 1761 1761 [vm] (daemon) (idle) softwareupdatese <2bc4b5ae016431c98d3b34f81027d0ae> 311 311 [vm] (daemon) (idle)Amazon <4600481f07ec3e59a925319b7f67ba14> 2951 2951 [vm] (suspended)accountsd <ac0fce15c1a2350d951efc498d521ac7> 519 519 [vm] (daemon) (idle) coresymbolicatio <edba67001f76313b992056c712153b4b> 126 126 [vm] (daemon) (idle)Skype <504cf2fe60cb3cdea8273e74df09836b> 3187 3187 [vm] (background)MobileMail <bff817c61ce33c85a43ea9a6c98c29f5> 14927 14927 [vm] (continuous)MobileSMS <46778de076363d67aeea207464cfc581> 2134 2134 [vm] (background)MobilePhone <3fca241f2a193d0fb8264218d296ea41> 2689 2689 [vm] (continuous)librariand <c9a9be81aa9632f0a913ce79b911f27e> 317 317 [vm] (daemon)kbd <3e7136ddcefc3d77a01499db593466cd> 616 616 [vm] (daemon)tccd <eb5ddcf533663f8d987d67cae6a4c4ea> 224 224 [vm] (daemon)Rage Masters <90b45d6281e934209c5b06cf7dc4d492> 28591 28591 [vm] (frontmost) (resume)ptpd <04a56fce67053c57a7979aeea8e5a7ea> 879 879 (daemon)iaptransportd <f784f30dc09d32078d87b450e8113ef6> 230 230 (daemon)locationd <892cd1c9ffa43c99a82dba197be5f09e> 1641 1641 (daemon)syslogd <cbef142fa0a839f0885afb693fb169c3> 237 237 (daemon)mediaserverd <80657170daca32c9b8f3a6b1faac43a2> 4869 4869 (daemon)dataaccessd <2a3f6a518f3f3646bf35eddd36f25005> 1786 1786 (daemon)aosnotifyd <d4d14f2914c3343796e447cfef3e6542> 549 549 (daemon)wifid <9472b090746237998cdbb9b34f090d0c> 455 455 (daemon)SpringBoard <27372aae101f3bbc87804edc10314af3> 18749 18749 backboardd <5037235f295b33eda98eb5c72c098858> 5801 5801 (daemon)UserEventAgent <6edfd8d8dba23187b05772dcdfc94f90> 601 601 (daemon)mediaremoted <4ff39c50c684302492e396ace813cb25> 293 293 (daemon)pasteboardd <8a4279b78e4a321f84a076a711dc1c51> 176 176 (daemon) springboardservi <ff6f64b3a21a39c9a1793321eefa5304> 0 0 (daemon)syslog_relay <45e9844605d737a08368b5215bb54426> 0 0 (daemon)DTMobileIS <23303ca402aa3705870b01a9047854ea> 0 0 (daemon) notification_pro <845b7beebc8538ca9ceef731031983b7> 169 169 (daemon)syslog_relay <45e9844605d737a08368b5215bb54426> 0 0 (daemon)ubd <74dc476d1785300e9fcda555fcb8d774> 976 976 (daemon)twitterd <4b4946378a9c397d8250965d17055b8e> 730 730 (daemon)configd <4245d73a9e96360399452cf6b8671844> 809 809 (daemon)absinthed.N94 <7f4164c844fa340caa940b863c901aa9> 99 99 (daemon) filecoordination <fbab576f37a63b56a1039153fc1aa7d8> 226 226 (daemon)distnoted <a89af76ec8633ac2bbe99bc2b7964bb0> 137 137 (daemon)apsd <94d8051dd5f5362f82d775bc279ae608> 373 373 (daemon)networkd <0032f46009f53a6c80973fe153d1a588> 219 219 (daemon)aggregated <8c3c991dc4153bc38aee1e841864d088> 112 112 (daemon)BTServer <c92fbd7488e63be99ec9dbd05824f5e5> 522 522 (daemon)fairplayd.N94 <7bd896bd00783a48906090d05cf1c86a> 210 210 (daemon)fseventsd <996cc4ca03793184aea8d781b55bce08> 384 384 (daemon)imagent <1e68080947be352590ce96b7a1d07b2f> 586 586 (daemon)mDNSResponder <3e557693f3073697a58da6d27a827d97> 295 295 (daemon)lockdownd <ba1358c7a8003f1b91af7d5f58dd5bbe> 389 389 (daemon)powerd <2d2ffed5e69638aeba1b92ef124ed861> 174 174 (daemon)CommCenter <1f425e1e897d32e8864fdd8eeaa803a8> 2212 2212 (daemon)notifyd <51c0e03da8a93ac8a595442fcaac531f> 211 211 (daemon)ReportCrash <8c32f231b2ed360bb151b2563bcaa363> 337 337 (daemon) |
這日志跟我們前面見到的相差非常多。
這個(gè)一個(gè)來自iOS 6的低內(nèi)存崩潰日志。
正如我們前面所說的,低內(nèi)存崩潰日志與其它類型的崩潰日志非常不一樣,它們不指向特定的文件和代碼行。
相反,它們畫出了閃退時(shí)設(shè)備上的內(nèi)存使用情況的圖表。
至少,頭部還是跟其它崩潰日志非常像的: ?提供了 Incident Identifier, CrashReporter Key, Hardware Model, OS Version等信息。
接下來部分是低內(nèi)存崩潰日志特有的:
- Free pages?指可用內(nèi)存頁數(shù)。每頁大小約是4KB, 上面的日志中,可用內(nèi)存約為3,872 KB (或者說 3.9 MB)。
- Purgeable pages?是那部分可被清除或重用的內(nèi)存。在上面的日志中,是0KB。
- Largest process是閃退時(shí)使用大部分內(nèi)存的應(yīng)用名稱。在上面的日志中,正是你的應(yīng)用!
- Processes顯示了閃退時(shí)各進(jìn)程列表,還包括內(nèi)存使用量。
包括進(jìn)程名 (第一列), 進(jìn)程唯一標(biāo)識(shí)符(第二名), 進(jìn)程使用的內(nèi)存頁數(shù)(第三列)。最后一列是每一個(gè)應(yīng)用的狀態(tài)。
通常,發(fā)生閃退的應(yīng)用的狀態(tài)是 frontmost。 這里是 Rage Masters, 使用28591 頁 (or 114.364 MB) 內(nèi)存——這內(nèi)存太多了!
通過,最大進(jìn)程和frontmost狀態(tài)的應(yīng)用是同樣的, 并且也是引起低內(nèi)存閃退的應(yīng)用進(jìn)程。
可是也可能看到最大進(jìn)程和 frontmost狀態(tài)應(yīng)用不同的樣例。比方,假設(shè)最大進(jìn)程是SpringBoard, 忽略它 , 由于 SpringBoard 進(jìn)程是顯示主屏幕的應(yīng)用。出如今你雙擊homebutton等情況,并且它是一直活動(dòng)的。
低內(nèi)存發(fā)生時(shí),iOS向活動(dòng)的應(yīng)用發(fā)出低內(nèi)存警告并終止后臺(tái)應(yīng)用。假設(shè)前臺(tái)應(yīng)用仍然繼續(xù)增長內(nèi)存,iOS將終止它。
為了查找低內(nèi)存問題的解決辦法,你必需使用Instruments剖析應(yīng)用。假設(shè)你不知道怎么做,能夠看一下我們 一篇關(guān)于這個(gè)方面的教程.。 :] 另外, 你也能夠走捷徑。響應(yīng)低內(nèi)存警告通知,以解決部分閃退問題。
回到Xcode查看RMLollipopLicker.m文件。 這是實(shí)現(xiàn)吃棒棒糖的視圖控制器。看看源碼:
| #import "RMLollipopLicker.h"#define COUNT 20@interface RMLollipopLicker () @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (weak, nonatomic) IBOutlet UILabel *label; @property (weak, nonatomic) IBOutlet UILabel *lickedTimeLabel; @end@implementation RMLollipopLicker { NSOperationQueue *queue; NSMutableArray *lollipops; }#pragma mark - Life cycle- (void)viewDidLoad { [super viewDidLoad];self.progressView.progress = 0.0; self.label.text = [NSString stringWithFormat:@"Tap on run and I'll lick a lollipop %d times!", COUNT]; self.lickedTimeLabel.text = @"";lollipops = [[NSMutableArray alloc] init]; queue = [[NSOperationQueue alloc] init]; }- (void)lickLollipop { NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Lollipop" withExtension:@"plist"]; NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:fileURL]; NSString *lollipop = [dictionary objectForKey:@"Lollipop"]; [lollipops addObject:lollipop]; }#pragma mark - IBActions- (IBAction)doneButtonPressed:(id)sender {[self dismissViewControllerAnimated:YES completion:nil]; }- (IBAction)runButtonPressed:(id)sender {[sender setEnabled:NO]; [queue addOperationWithBlock:^{for (NSInteger i = 0 ; i = COUNT) { self.label.text = [NSString stringWithFormat:@"Tap on run and I'll lick a lollipop %d times!", COUNT]; self.progressView.progress = 0.0; [sender setEnabled:YES]; } }]; } }];}@end |
當(dāng)用戶點(diǎn)擊執(zhí)行button, 應(yīng)用開始一個(gè)背景線程。調(diào)用 lickLollipop 方法若干次,然后更新界面反映吃棒棒糖的數(shù)量。 lickLollipop 方法從屬性列表文件(PLIST)文件讀取一個(gè)長字符串,然后加入到數(shù)組上。這些數(shù)據(jù)并不重要, 能在不影響用戶體驗(yàn)的前提下又一次創(chuàng)建。
利用每種可以清除和重建數(shù)據(jù)而不影響用戶體驗(yàn)的情況是好習(xí)慣。
這樣可以方便地釋放內(nèi)存,降低低內(nèi)存警告。
那么,怎樣提高代碼質(zhì)量呢? 實(shí)現(xiàn) didReceiveMemoryWarning 方法。像以下這樣處理數(shù)據(jù):
| -(void)didReceiveMemoryWarning { [lollipops removeAllObjects]; [super didReceiveMemoryWarning]; } |
搞定!
下一步?
萬歲,你研究了4個(gè)閃退案例! 你的應(yīng)用更完好了,而且學(xué)到了一些重要的調(diào)試技巧。
你能夠到這里下載改進(jìn)后的項(xiàng)目代碼。
轉(zhuǎn)載于:https://www.cnblogs.com/lytwajue/p/7183726.html
總結(jié)
以上是生活随笔為你收集整理的iOS应用崩溃日志分析 iOS应用崩溃日志揭秘的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lintcode-102-带环链表
- 下一篇: OpenCV中findContours函