當(dāng)一個(gè)應(yīng)用發(fā)生崩潰時(shí)會(huì)產(chǎn)生一份崩潰報(bào)告(Crash Report)饲齐,該報(bào)告可以幫助我們了解崩潰的產(chǎn)生原因钾怔。該文檔講述了關(guān)于怎么樣符號(hào)化鳍刷、理解和分析崩潰報(bào)告的相關(guān)內(nèi)容租漂。
- 介紹
- 獲取崩潰和低內(nèi)存報(bào)告
- 分析崩潰報(bào)告
- 頭部信息
- 異常代碼
- 應(yīng)用具體信息
- 回溯
- 線程狀態(tài)
- 二進(jìn)制映像
- 了解低內(nèi)存報(bào)告
- 相關(guān)文檔
- 文檔修改記錄
介紹
當(dāng)iOS設(shè)備上的應(yīng)用崩潰時(shí)喇伯,設(shè)備上會(huì)為其生成和保存一份崩潰報(bào)告喊儡。崩潰報(bào)告描述了應(yīng)用程序在什么情況下結(jié)束運(yùn)行的。在大多數(shù)情況下報(bào)告會(huì)為每個(gè)執(zhí)行的線程包含一個(gè)完整的回溯(Backtrace)稻据,這對(duì)于調(diào)試應(yīng)用崩潰問題時(shí)非常有用艾猜。如果你是iOS開發(fā)者,你應(yīng)該查看這些崩潰報(bào)告來了解你的應(yīng)用存在哪些崩潰捻悯,并且應(yīng)該對(duì)這些崩潰進(jìn)行修復(fù)匆赃。
崩潰報(bào)告中帶有的回溯必須要先進(jìn)行符號(hào)化(Symbolicated)才可以進(jìn)行分析。符號(hào)化(Symbolication)指的是使用人能夠讀懂的方法名稱和行號(hào)來替換回溯里面的內(nèi)存地址信息今缚。如果你通過XCode的Devices窗口獲得一臺(tái)設(shè)備的崩潰日志算柳,那么它會(huì)在幾秒鐘內(nèi)自動(dòng)地將日志進(jìn)行符號(hào)化。否則你需要手動(dòng)將.crash文件導(dǎo)入到XCode的Devices窗口中進(jìn)行符號(hào)化姓言〔壕樱可以參考符號(hào)化(Symbolication)章節(jié)來了解詳細(xì)的內(nèi)容
低內(nèi)存報(bào)告與其它崩潰報(bào)告不同的地方在于它沒有回溯信息查牌。當(dāng)一個(gè)低內(nèi)存崩潰發(fā)生時(shí),你必須檢查你的內(nèi)存使用圖表(在Debug面板的Memory中查看)并且對(duì)低內(nèi)存警告做出應(yīng)對(duì)處理(如UIViewController中的didReceiveMemoryWarning)滥壕。本篇文檔會(huì)提供給你一些內(nèi)存管理方面的參考資料纸颜,你應(yīng)該能夠在里面學(xué)習(xí)到一些有用的信息。
獲取崩潰和低內(nèi)存報(bào)告
《調(diào)試部署iOS應(yīng)用》一文中講述了怎樣直接從一臺(tái)iOS設(shè)備中查找崩潰和低內(nèi)存報(bào)告绎橘。
《應(yīng)用分發(fā)向?qū)В治霰罎?bào)告》一文中講述了怎么查看從TestFlight的beta測(cè)試用戶和從AppStore下載你的App的用戶那里所收集到的崩潰日志信息胁孙。
分析崩潰報(bào)告
本章節(jié)主要討論一份標(biāo)準(zhǔn)的崩潰報(bào)告中出現(xiàn)的每個(gè)小節(jié)。
頭部信息(Header)
每份崩潰報(bào)告的開頭都帶有一個(gè)頭部信息称鳞。
Incident Identifier: E6EBC860-0222-4B82-BF7A-2B1C26BE1E85
CrashReporter Key: 6196484647b3431a9bc2833c19422539549f3dbe
Hardware Model: iPhone6,1
Process: TheElements [4637]
Path: /private/var/mobile/Containers/Bundle/Application/5A9E4FC2-D03B-4E19-9A91- 104A0D0C1D44/TheElements.app/TheElements
Identifier: com.example.apple-samplecode.TheElements
Version: 1.12
Code Type: ARM (Native)
Parent Process: launchd [1]
Date/Time: 2015-04-06 09:14:08.775 -0700
Launch Time: 2015-04-06 09:14:08.597 -0700
OS Version: iOS 8.1.3 (12B466)
Report Version: 105
清單1.從某份崩潰報(bào)告中摘錄下來的頭部信息
大多數(shù)字段的意思都很容易理解涮较,有少數(shù)字段需要特殊說明一下:
- Incident Identifier:報(bào)告的唯一標(biāo)識(shí)。不同的報(bào)告該值不相同冈止。
- CrashReporter Key:每臺(tái)設(shè)備的匿名標(biāo)識(shí)(并非真正的設(shè)備標(biāo)識(shí))狂票。如果不同的報(bào)告出自同一設(shè)備,則該值相同熙暴。
- Process:崩潰進(jìn)程的名稱闺属。該值對(duì)應(yīng)應(yīng)用信息屬性列表(Info.plist)中的CFBundleExecutable所指定的值。
- Version:崩潰進(jìn)程的版本號(hào)周霉。該值對(duì)應(yīng)應(yīng)用信息屬性列表(Info.plist)中的CFBundleVersion和CFBundleVersionString拼接起來的值掂器。
- Code Type:崩潰進(jìn)程的目標(biāo)體系結(jié)構(gòu)。該值是ARM-64或ARM中的一個(gè)俱箱。
- OS Version:發(fā)生崩潰的操作系統(tǒng)版本號(hào)(包括構(gòu)建版本號(hào))国瓮。
異常代碼(Exception Codes)
這里不要和Objective-C/C++的異常混淆了(雖然兩者都有可能是崩潰的原因)狞谱。本章節(jié)列出了Mach的異常類型乃摹、子異常類型、處理器專用異常代碼和其它的一些字段來為崩潰的根本原因提供更多的信息跟衅。最后一個(gè)字段列出了觸發(fā)崩潰的線程的索引孵睬。并不是所有字段都會(huì)在每份崩潰報(bào)告中出現(xiàn)。
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread: 0
清單2.從某份崩潰報(bào)告中摘錄的異常代碼
下面的章節(jié)將對(duì)一些公共的異常類型進(jìn)行說明与斤。
壞的內(nèi)存訪問 [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]
進(jìn)程試圖訪問無效的內(nèi)存地址. 子異常類型字段包含一個(gè)kern_return_t的錯(cuò)誤描述. 子異常代碼(該值跟在子異常類型的后面) 列出被訪問的壞內(nèi)存地址肪康。
如果objc_msgSend或者objc_release出現(xiàn)接近崩潰線程的回溯頂部, 進(jìn)程可能試圖向已經(jīng)釋放的對(duì)象發(fā)送消息. 你應(yīng)該通過Instrument工具的Zombies來分析你的應(yīng)用,以便更好地了解觸發(fā)該崩潰的條件撩穿。
不正常退出[EXC_CRASH // SIGABRT]
進(jìn)程不正常退出. 導(dǎo)致該異常的大多數(shù)情況是因?yàn)闆]有捕獲Objective - C/C++所產(chǎn)生的異常磷支。
如果應(yīng)用擴(kuò)展(App Extensions)在初始化時(shí)花費(fèi)太多時(shí)間將會(huì)被結(jié)束掉(watchdog結(jié)束,watchdog結(jié)束指的是watchdog超時(shí)后強(qiáng)行終止應(yīng)用)食寡。如果擴(kuò)展在啟動(dòng)時(shí)由于掛起而被殺掉進(jìn)程雾狈,那么崩潰報(bào)告的子異常類型將會(huì)是LAUNCH_HANG。因?yàn)閿U(kuò)展沒有主函數(shù)抵皱,因此初始化花費(fèi)的時(shí)間都體現(xiàn)在擴(kuò)展和依賴庫的靜態(tài)構(gòu)造函數(shù)和+load方法中善榛。你應(yīng)該盡可能地避免在靜態(tài)構(gòu)造函數(shù)和+load方法中處理過多的工作(也就是文檔中說的延遲大部分的工作處理)辩蛋。
跟蹤陷阱 [EXC_BREAKPOINT // SIGTRAP]
跟非正常退出相似。該異常是由于打算給一個(gè)附加的調(diào)試器在執(zhí)行特定的斷點(diǎn)來中斷進(jìn)程時(shí)觸發(fā)移盆。你可以在自己的代碼中使用__builtin_trap()方法來觸發(fā)此異常悼院。如果沒有被調(diào)試器所附加,那么進(jìn)程將會(huì)結(jié)束并且生成一份崩潰報(bào)告咒循。
如果Swift代碼在運(yùn)行時(shí)發(fā)現(xiàn)一個(gè)意外的情況時(shí)据途,也會(huì)以該異常類型結(jié)束程序。例如:
- 為一個(gè)非可選(non-optional)類型被賦值nil
- 一個(gè)有問題的強(qiáng)制類型轉(zhuǎn)換
通過看崩潰線程的回溯來確定是在什么位置出現(xiàn)異常的情況叙甸。
守護(hù)資源的侵害 [EXC_GUARD]
進(jìn)程侵害了一個(gè)被守護(hù)的資源颖医。系統(tǒng)庫可能會(huì)標(biāo)記守護(hù)某些文件描述符,在此之后如果對(duì)這些文件描述符作正常的操作將觸發(fā)EXC_GUARD異常(當(dāng)系統(tǒng)想操作這些文件描述符時(shí)裆蒸,會(huì)使用特殊的'guarded'私有API)熔萧。該異常類型可以幫助你快速地跟蹤例如關(guān)閉一個(gè)由系統(tǒng)庫打開的文件描述符這類的問題。舉個(gè)例子僚祷,如果應(yīng)用關(guān)閉了一個(gè)基于SQLite存儲(chǔ)的Core Data的SQLite文件描述符佛致,那么Core Data隨后也會(huì)離奇地崩潰。守護(hù)異常越早發(fā)現(xiàn)越容易調(diào)試久妆。
相關(guān)的子異常類型是一個(gè)位字段(bitfield)晌杰,分解如下:
- [63:61] ‐ 守護(hù)類型: 守護(hù)資源的類型. 0x2表示一個(gè)文件描述符跷睦。
-
[60:32] ‐ 特點(diǎn)(Flavor): 違規(guī)行為被觸發(fā)的條件筷弦。
- 如果第一位(1 << 0)被設(shè)置,則表示進(jìn)程試圖在被守護(hù)的文件描述符上調(diào)用close()
- 如果第二位(1 << 1)被設(shè)置抑诸,則表示進(jìn)程試圖在一個(gè)被守護(hù)的文件描述符上調(diào)用dup()烂琴、dup2() 、帶有F_DUPFD或者F_DUPFD_CLOEXEC命令的fcntl()方法蜕乡。
- 如果第三位(1 << 2)被設(shè)置奸绷,則表示進(jìn)程試圖通過套接字(Socket)來發(fā)送一個(gè)被守護(hù)的文件描述符。
- 如果第五位(1 << 4)被設(shè)置层玲,則表示進(jìn)程試圖對(duì)一個(gè)被守護(hù)的文件描述進(jìn)行寫操作号醉。
- [31:0] ‐文件描述符:進(jìn)程試圖修改的被守護(hù)的文件描述符。
資源限制 [EXC_RESOURCE]
進(jìn)程觸及了消耗資源的限額辛块。這不是一個(gè)崩潰畔派,但系統(tǒng)會(huì)派發(fā)通知告訴進(jìn)程使用了過多的資源。在子異常類型中會(huì)描述確切的資源信息:
子異常類型WAKEUPS表示一個(gè)線程每秒中喚醒的次數(shù)太多润绵,這迫使CPU喚醒太頻繁并且會(huì)損耗電池的壽命线椰。
子異常類型MEMORY表示進(jìn)程已經(jīng)越過了系統(tǒng)強(qiáng)制的內(nèi)存限制。這可能是過多占用內(nèi)存而導(dǎo)致崩潰的前兆尘盼。
其它異常類型
一些崩潰報(bào)告可能包含一個(gè)未命名的異常類型憨愉,是一個(gè)十六進(jìn)制數(shù)(如:00000020)烦绳。如果你收到這樣的崩潰報(bào)告,直接查看下面更多的異常代碼信息:
0xbaaaaaad表示日志是整個(gè)系統(tǒng)的一個(gè)堆椗渥希快照(stackshot)而不是一個(gè)崩潰報(bào)告径密。通過按下Home鍵和任意音量鍵可以獲得堆棧快照(stackshot)躺孝。通常這些日志是被用戶意外創(chuàng)建的(不小心同時(shí)按下了Home鍵+音量鍵)睹晒,而不是一個(gè)錯(cuò)誤。
0xbad22222表示一個(gè)VoIP應(yīng)用由于恢復(fù)過于頻繁而被iOS結(jié)束進(jìn)程括细。
0x8badf00d表示一個(gè)應(yīng)用由于watchdog發(fā)生超時(shí)而被iOS結(jié)束進(jìn)程伪很。這表明該應(yīng)用程序在啟動(dòng)、結(jié)束或者響應(yīng)系統(tǒng)事件時(shí)花費(fèi)太長時(shí)間。一個(gè)常見的例子是在主線程上實(shí)現(xiàn)同步的網(wǎng)絡(luò)操作。任何在Thread 0(主線程)上的操作都應(yīng)該放到后臺(tái)線程上執(zhí)行倒慧,或者使用不阻塞主線程的方式進(jìn)行處理缝驳。
0xc00010ff表示一個(gè)應(yīng)用由于響應(yīng)一個(gè)熱事件(thermal event)而被操作系統(tǒng)殺掉進(jìn)程。這可能是由于在特定的設(shè)備上發(fā)生崩潰呢灶,或者是操作環(huán)境的問題。為了讓你的程序獲得更多有效的提示,請(qǐng)觀看《使用Instruments來提升iOS性能和電量優(yōu)化的WWDC會(huì)議》应又。
0xdead10cc表示一個(gè)應(yīng)用由于在后臺(tái)運(yùn)行時(shí)還保留著系統(tǒng)的資源(如通訊錄數(shù)據(jù)庫)而被iOS結(jié)束進(jìn)程。
0xdeadfa11表示應(yīng)用程序被用戶強(qiáng)制退出乏苦。當(dāng)用戶先按住開機(jī)鍵直到出現(xiàn)“滑動(dòng)來關(guān)機(jī)”界面后再按住Home一段時(shí)間后就會(huì)出現(xiàn)強(qiáng)制退出的情況株扛。這可能由于應(yīng)用程序無法響應(yīng)才使用這種方法來進(jìn)行強(qiáng)制退出,但是不能保證可以退出所有應(yīng)用程序汇荐。
注意:從多任務(wù)列表中直接結(jié)束一個(gè)掛起的應(yīng)用是不會(huì)產(chǎn)生崩潰報(bào)告的洞就。一旦應(yīng)用掛起,則iOS可以在任何時(shí)候?qū)⑵浣Y(jié)束掀淘,而不產(chǎn)生崩潰報(bào)告旬蟋。
應(yīng)用具體信息(Application Specific Information)
某些崩潰會(huì)寫入一些額外的信息到崩潰報(bào)告中。不同的結(jié)束類型對(duì)應(yīng)該部分的內(nèi)容也不相同革娄。你可以通過閱讀本章內(nèi)容來更好地了解一下應(yīng)用程序結(jié)束的情形倾贰。
Application Specific Information:
MyApp[134] was suspended with locked system files:
/private/var/mobile/Library/AddressBook/AddressBook.sqlitedb
清單3.從某份崩潰報(bào)告中摘錄的應(yīng)用具體信息
回溯(Backtrace)
奔潰報(bào)告中最有趣的部分就是每個(gè)進(jìn)程當(dāng)時(shí)已經(jīng)中止執(zhí)行的線程回溯。這些回溯的內(nèi)容就和你使用調(diào)試器停止執(zhí)行應(yīng)用的時(shí)候看到的內(nèi)容類似拦惋。
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 TheElements 0x0000000100063fdc -[AtomicElementViewController myTransitionDidStop:finished:context:] (AtomicElementViewController.m:201)
1 UIKit 0x000000018ca5c2ec -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 184
2 UIKit 0x000000018ca5c1f4 -[UIViewAnimationState animationDidStop:finished:] + 100
3 QuartzCore 0x000000018c380f60 CA::Layer::run_animation_callbacks(void*) + 292
4 libdispatch.dylib 0x0000000198fb9368 _dispatch_client_callout + 12
5 libdispatch.dylib 0x0000000198fbd97c_dispatch_main_queue_callback_4CF + 928
6 CoreFoundation 0x000000018822dfa0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
7 CoreFoundation 0x000000018822c048 __CFRunLoopRun + 1488
8 CoreFoundation 0x00000001881590a0 CFRunLoopRunSpecific + 392
9 GraphicsServices 0x00000001912fb5a0 GSEventRunModal + 164
10 UIKit 0x000000018ca8aaa0 UIApplicationMain + 1484
11 TheElements 0x000000010005d800 main (main.m:55)
12 libdyld.dylib 0x0000000198fe2a04 start + 0
Thread 1 name: Dispatch queue: com.apple.libdispatch-manager
Thread 1:
0 libsystem_kernel.dylib 0x00000001990e0c94 kevent64 + 8
1 libdispatch.dylib 0x0000000198fc897c_dispatch_mgr_invoke + 272
2 libdispatch.dylib 0x0000000198fbb3b0_dispatch_mgr_thread + 48
...
清單4. 從某份已完成符號(hào)化的崩潰報(bào)告中摘錄的回溯內(nèi)容
符號(hào)化(Symbolication)
從iOS設(shè)備中檢索到的崩潰日志只有可執(zhí)行代碼在加載的二進(jìn)制映像(Binary Images)中的十六進(jìn)制地址匆浙,是沒有包含方法或函數(shù)名稱的,而這些方法和函數(shù)的名稱被稱為符號(hào)架忌,如清單5所示吞彤,你需要將這些地址與符號(hào)進(jìn)行映射。
將回溯的地址解析為源碼的方法和行號(hào)被稱為符號(hào)化 ,這過程需要上傳到AppStore的應(yīng)用的二進(jìn)制文件和編譯二進(jìn)制文件時(shí)生成的.dSYM文件饰恕。二進(jìn)制文件和.dSYM文件是一一對(duì)應(yīng)的挠羔,否則崩潰報(bào)告可能只會(huì)顯示部分內(nèi)容被符號(hào)化,如清單6所示埋嵌。因此破加,保留每個(gè)分發(fā)給用戶的應(yīng)用包(不管如何分發(fā))和對(duì)應(yīng)的.dSYM文件就非常有必要了。
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 TheElements 0x00000001000effdc0x1000e4000+49116
1 UIKit 0x000000018ca5c2ec 0x18ca14000 + 295660
2 UIKit 0x000000018ca5c1f4 0x18ca14000 + 295412
3 QuartzCore 0x000000018c380f600x18c36c000 + 85856
4 libdispatch.dylib 0x0000000198fb93680x198fb8000 + 4968
5 libdispatch.dylib 0x0000000198fbd97c0x198fb8000 + 22908
6 CoreFoundation 0x000000018822dfa0 0x188150000 + 909216
7 CoreFoundation 0x000000018822c048 0x188150000 + 901192
8 CoreFoundation 0x00000001881590a00x188150000 + 37024
9 GraphicsServices 0x00000001912fb5a00x1912f0000 + 46496
10 UIKit 0x000000018ca8aaa0 0x18ca14000 + 486048
11 TheElements 0x00000001000e98000x1000e4000 + 22528
12 libdyld.dylib 0x0000000198fe2a040x198fe0000 + 10756
清單5.從某份未符號(hào)化的崩潰報(bào)告中摘錄的回溯部分
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 TheElements 0x00000001000effdc0x1000e4000 + 49116
1 UIKit 0x000000018ca5c2ec -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 184
2 UIKit 0x000000018ca5c1f4 -[UIViewAnimationState animationDidStop:finished:] + 100
3 QuartzCore 0x000000018c380f60 CA::Layer::run_animation_callbacks(void*) + 292
4 libdispatch.dylib 0x0000000198fb9368_dispatch_client_callout + 12
5 libdispatch.dylib 0x0000000198fbd97c_dispatch_main_queue_callback_4CF + 928
6 CoreFoundation 0x000000018822dfa0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
7 CoreFoundation 0x000000018822c048__CFRunLoopRun + 1488
8 CoreFoundation 0x00000001881590a0CFRunLoopRunSpecific + 392
9 GraphicsServices 0x00000001912fb5a0GSEventRunModal + 164
10 UIKit 0x000000018ca8aaa0UIApplicationMain + 1484
11 TheElements 0x00000001000e98000x1000e4000 + 22528
12 libdyld.dylib 0x0000000198fe2a04start + 0
清單6.從某份部分符號(hào)化崩潰報(bào)告中摘錄的回溯部分(系統(tǒng)的棧幀已符號(hào)化雹嗦,但沒有符號(hào)化應(yīng)用程序棧幀)
要點(diǎn):你必須保留應(yīng)用程序二進(jìn)制和它對(duì)應(yīng)的.dSYM文件才能夠完整地進(jìn)行符號(hào)化范舀。你每次提交這些編譯文件到iTunes Connect的時(shí)候必須要將它們歸檔。.dSYM文件和應(yīng)用程序的二進(jìn)制文件在每次編譯中是對(duì)應(yīng)捆綁的了罪。即使是往后使用的是相同的源文件進(jìn)行編譯锭环,那產(chǎn)生的.dSYM文件和應(yīng)用程序的二進(jìn)制文件也是跟之前的沒有任何關(guān)系的。如果你使用XCode的Build和Archive命令進(jìn)行編譯泊藕,那么.dSYM 文件和二進(jìn)制文件將會(huì)自動(dòng)放到一個(gè)合適的路徑辅辩。不然也可以是一個(gè)通過Spotlight可搜索到的位置(如你的Home目錄)。
使用XCode的Archive命令可以很容易地使二進(jìn)制文件和.dSYM文件配對(duì)娃圆。當(dāng)你使用Archive命令(通過選擇Product菜單中的Archive)時(shí)玫锋,XCode會(huì)一起生成應(yīng)用的二進(jìn)制文件和包含符號(hào)信息的.dSYM文件并將它們保存到你的Home目錄下。你可以通過XCode的Organizer在Archived欄目下找到所有你歸檔過的應(yīng)用讼呢。當(dāng)符號(hào)化崩潰報(bào)告時(shí)撩鹿,XCode會(huì)自動(dòng)從這里查找對(duì)應(yīng)的歸檔應(yīng)用;并且可以通過這里直接提交歸檔應(yīng)用到iTunes Connect來確保你release的應(yīng)用程序二進(jìn)制文件和.dSYM文件相匹配悦屏。
XCode會(huì)自動(dòng)符號(hào)化所有能夠匹配程序二進(jìn)制文件和.dSYM文件的崩潰報(bào)告节沦。因此,你需要將所有要符號(hào)化的崩潰報(bào)告添加到XCode的Organizer中窜管。其步驟如下:
- 將iOS設(shè)備連接到你的Mac中散劫。
- 選擇XCode的Window菜單中的Devices稚机。
- 在左側(cè)的DEVICES欄目下選擇連接的設(shè)備幕帆。
- 在右側(cè)的Device Information欄目下點(diǎn)擊“View Device Logs”按鈕。
- 將你的崩潰報(bào)告拖拽到彈出面板的左側(cè)赖条。
- XCode會(huì)自動(dòng)符號(hào)化崩潰報(bào)告并顯示符號(hào)化后的結(jié)果失乾。
異常(Exceptions)
異常在Objective-C中用來說明編程中或者運(yùn)行時(shí)意外發(fā)生的錯(cuò)誤。例如:在集合中的超出范圍訪問纬乍、試圖改變一個(gè)不可變的對(duì)象碱茁、沒有實(shí)現(xiàn)協(xié)議中必須實(shí)現(xiàn)的方法或者給對(duì)象發(fā)送一個(gè)不存在的消息等。
注意:給一個(gè)已經(jīng)釋放的對(duì)象發(fā)送消息會(huì)拋出NSInvalidArgumentException異常而不是立即崩潰仿贬;當(dāng)一個(gè)新對(duì)象分配的內(nèi)存剛好在已釋放對(duì)象的內(nèi)存地址上時(shí)會(huì)發(fā)生這樣的情況纽竣。如果你的應(yīng)用崩潰是由于未捕獲的NSInvalidArgumentException異常(在異常的回溯中看到有-[NSObject(NSObject) doesNotRecognizeSelector:]這樣的信息),可以考慮使用Instrument的Zombies分析你的應(yīng)用程序來盡可能排出一些不合理的內(nèi)存管理情況。
如果一個(gè)異常未被捕獲蜓氨,那么它會(huì)被一個(gè)叫未捕獲異常處理器(uncaught exception handler)的方法所攔截聋袋。iOS默認(rèn)的未捕獲異常處理器會(huì)將異常信息和回溯打印到設(shè)備的控制臺(tái)后結(jié)束掉程序。只有最后一個(gè)未捕獲異常會(huì)寫入到崩潰報(bào)告的Last Exception Backtrace小節(jié)下穴吹,如清單7所示幽勒。崩潰報(bào)告中省略了異常消息。如果你收到一份帶有Last Exception Backtrace小節(jié)的崩潰報(bào)告港令,你應(yīng)該從原來的設(shè)備獲得控制臺(tái)日志來更好地了解導(dǎo)致拋出這次異常的情況啥容。
Last Exception Backtrace:
(0x18632c2d8 0x197af80e4 0x18632bf5c 0x187165480 0x186257520 0x18b18c7a0 0x18b088384
0x18ad6ca28 0x18ad6c994 0x18af0f25c 0x18ae21ef0 0x18ae21cbc 0x18ae21c3c 0x18ad69760
0x18a6b1e1c 0x18a6ac884 0x18a6ac728 0x18a6abebc 0x18a6abc3c 0x18a6a5364 0x1862e42a4
0x1862e1230 0x1862e1610 0x18620d2d4 0x18fa2b6fc 0x18add2fac 0x1000fd2f4 0x198176a08)
條目7.從某份未符號(hào)化的異常報(bào)告中摘錄的Last Exception Backtrace小節(jié)
帶有Last Exception Backtrace的崩潰日志僅包含了16進(jìn)制的地址信息,必須對(duì)其進(jìn)行符號(hào)化處理顷霹,使它變成可被理解的回溯咪惠。如條目8所示:
Last Exception Backtrace:
0 CoreFoundation 0x18632c2d8 __exceptionPreprocess + 132
1 libobjc.A.dylib 0x197af80e4 objc_exception_throw + 60
2 CoreFoundation 0x18632bf5c -[NSException raise] + 12
3 Foundation 0x187165480 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 248
4 CoreFoundation 0x186257520 -[NSArray makeObjectsPerformSelector:] + 248
5 UIKit 0x18b18c7a0 -[UINib instantiateWithOwner:options:] + 1604
6 UIKit 0x18b088384 -[UIViewController _loadViewFromNibNamed:bundle:] + 284
7 UIKit 0x18ad6ca28 -[UIViewController loadViewIfRequired] + 88
8 UIKit 0x18ad6c994 -[UIViewController view] + 32
9 UIKit 0x18af0f25c -[UINavigationController _startCustomTransition:] + 712
10 UIKit 0x18ae21ef0 -[UINavigationController _startDeferredTransitionIfNeeded:] + 468
11 UIKit 0x18ae21cbc -[UINavigationController __viewWillLayoutSubviews] + 56
12 UIKit 0x18ae21c3c -[UILayoutContainerView layoutSubviews] + 200
13 UIKit 0x18ad69760 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 580
14 QuartzCore 0x18a6b1e1c -[CALayer layoutSublayers] + 152
15 QuartzCore 0x18a6ac884 CA::Layer::layout_if_needed(CA::Transaction*) + 320
16 QuartzCore 0x18a6ac728 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32
17 QuartzCore 0x18a6abebc CA::Context::commit_transaction(CA::Transaction*) + 276
18 QuartzCore 0x18a6abc3c CA::Transaction::commit() + 528
19 QuartzCore 0x18a6a5364 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 80
20 CoreFoundation 0x1862e42a4 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
21 CoreFoundation 0x1862e1230 __CFRunLoopDoObservers + 360
22 CoreFoundation 0x1862e1610 __CFRunLoopRun + 836
23 CoreFoundation 0x18620d2d4 CFRunLoopRunSpecific + 396
24 GraphicsServices 0x18fa2b6fc GSEventRunModal + 168
25 UIKit 0x18add2fac UIApplicationMain + 1488
26 TheElements 0x1000fd2f4 main (main.m:55)
27 libdyld.dylib 0x198176a08 start + 4
清單8.從某份已符號(hào)化的崩潰報(bào)告中摘錄的Last Exception Backtrace小節(jié).這是一個(gè)在應(yīng)用的故事板中加載一個(gè)場(chǎng)景時(shí)拋出的異常。因?yàn)橐粋€(gè)與IBOutlet相關(guān)聯(lián)的場(chǎng)景元素缺失導(dǎo)致淋淀。
注意:如果發(fā)現(xiàn)你的應(yīng)用程序所指定的異常處理域中的異常在拋出時(shí)沒有被捕獲硝逢,請(qǐng)檢查你的應(yīng)用或者庫在編譯時(shí)是否指定了-no_compact_unwind標(biāo)識(shí),如果指定了請(qǐng)去掉绅喉。
64位的iOS使用了一個(gè)“零成本(zero-cost)”的異常實(shí)現(xiàn)渠鸽。在一個(gè)“零成本”系統(tǒng)中,每個(gè)可能拋出異常的方法都有描述怎么unwind堆棧的附加信息柴罐。如果一個(gè)異常在不具有unwind數(shù)據(jù)的棧幀中拋出徽缚,那么異常處理將無法繼續(xù)并且進(jìn)程被停止執(zhí)行。這有可能是一個(gè)更上層堆棧異常處理革屠,但是如果有一幀沒有unwind數(shù)據(jù)那么將沒有方法知道異常從哪一個(gè)棧幀拋出凿试。指定-no_compact_unwind標(biāo)識(shí)意味著你的方法沒有unwind table的代碼,所以你不能從這些方法中拋出異常似芝。
此外那婉,如果你的應(yīng)用程序或庫包含純C代碼,你可能需要指定-funwind-tables標(biāo)識(shí)來讓你的代碼中的所有方法包含unwind table党瓮。
線程狀態(tài)(Thread State)
該小節(jié)列出了崩潰線程的ARM狀態(tài)详炬。這是一份在崩潰時(shí)寄存器及其值的列表。當(dāng)你看一份崩潰報(bào)告的時(shí)候了解線程狀態(tài)不是必須的寞奸,但你可以利用這些信息來更好的了解當(dāng)時(shí)崩潰的情況呛谜。
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x00000001995f8020
x4: 0x0000000000000000 x5: 0x0000000000000001 x6: 0x0000000000000000 x7: 0x0000000000000000
x8: 0x0000000000000000 x9: 0x0000000000000015 x10: 0x0000000199601df0 x11: 0x0000000b0000000f
x12: 0x00000001741e8700 x13: 0x000001a5995f5779 x14: 0x0000000000000000 x15: 0x0000000044000000
x16: 0x00000001989724d8 x17: 0x0000000188176370 x18: 0x0000000000000000 x19: 0x00000001701dda60
x20: 0x0000000000000001 x21: 0x0000000136606e20 x22: 0x00000001000f6238 x23: 0x0000000000000000
x24: 0x000000019cc640a8 x25: 0x0000000000000020 x26: 0x0000000000000000 x27: 0x0000000000000000
x28: 0x000000019cc577c0 fp: 0x000000016fd1a8d0 lr: 0x00000001000effcc
sp: 0x000000016fd1a860 pc: 0x00000001000effdc cpsr: 0x60000000
清單9.從某份崩潰報(bào)告中摘錄的線程狀態(tài)小節(jié)
二進(jìn)制映像(Binary Images)
該小節(jié)列出了崩潰時(shí)加載到進(jìn)程中的二進(jìn)制映像。
Binary Images:
0x100058000 - 0x10006bfff TheElements arm64 <77b672e2b9f53b0f95adbc4f68cb80d6>
/var/mobile/Containers/Bundle/Application/CB86658C-F349-4C7A-B73B-CE3B4502D5A4/TheElements.app/TheElements
...
清單10.從某份崩潰報(bào)告中摘錄的二進(jìn)制映像小節(jié)中的程序入口
每行列出一個(gè)二進(jìn)制映像的以下細(xì)節(jié):
- 二進(jìn)制映像在進(jìn)程中的地址空間枪萄。
- 二進(jìn)制映像的可執(zhí)行程序名稱隐岛。
- 二進(jìn)制映像的體系結(jié)構(gòu)。一個(gè)可執(zhí)行文件可以包含多個(gè)不同體系結(jié)構(gòu)的“切片”瓷翻,但僅有一個(gè)“切片”將會(huì)加載到進(jìn)程中聚凹。
- 二進(jìn)制映像的唯一標(biāo)識(shí)UUID割坠。每個(gè)版本的應(yīng)用程序/框架和與其對(duì)應(yīng)的符號(hào)化.dSYM文件影響此標(biāo)識(shí)的變化。
- 可執(zhí)行文件在磁盤上的路徑妒牙。
了解低內(nèi)存報(bào)告
當(dāng)發(fā)現(xiàn)低內(nèi)存情況時(shí)韭脊,iOS中的虛擬內(nèi)存系統(tǒng)會(huì)依靠協(xié)作的應(yīng)用程序去釋放內(nèi)存。低內(nèi)存警告會(huì)通知所有運(yùn)行中的程序和進(jìn)程來請(qǐng)求釋放內(nèi)存单旁,希望減少內(nèi)存的使用量沪羔。如果內(nèi)存壓力得不到釋放,系統(tǒng)可能會(huì)終止后臺(tái)的進(jìn)程來緩解內(nèi)存壓力象浑。如果有足夠的內(nèi)存被釋放蔫饰,那么你的程序可以繼續(xù)運(yùn)行,否則你的程序會(huì)由于沒有足夠的內(nèi)存來滿足需要而被iOS結(jié)束掉愉豺,并且會(huì)在設(shè)備上生成和保存一份低內(nèi)存報(bào)告篓吁。
低內(nèi)存報(bào)告的格式和其它的崩潰報(bào)告不一樣,它沒有應(yīng)用的線程回溯蚪拦;其開始帶有一個(gè)類似于崩潰報(bào)告的頭部信息杖剪,接下來就是整個(gè)系統(tǒng)的內(nèi)存統(tǒng)計(jì)字段的集合。值得關(guān)注的是一個(gè)叫Page SIze字段的值驰贷,其記錄了關(guān)于每個(gè)進(jìn)程在低內(nèi)存報(bào)告中的使用內(nèi)存分頁數(shù)量方面的情況盛嘿。
低內(nèi)存報(bào)告中最重要的部分就是進(jìn)程列表(table of processes)。該表列出了在低內(nèi)存報(bào)告生成時(shí)所有正在運(yùn)行的進(jìn)程括袒,包括系統(tǒng)的守護(hù)進(jìn)程次兆。如果一個(gè)進(jìn)程被“拋棄(jettisoned)”,其原因?qū)?huì)記錄在[reason]列中锹锰。進(jìn)程被“拋棄”可能由于下面的原因?qū)е拢?/p>
- [per‐process‐limit]:該進(jìn)程跨越了系統(tǒng)施加的內(nèi)存限制芥炭。系統(tǒng)為所有應(yīng)用建立的常駐內(nèi)存進(jìn)行了限制∈鸦郏跨越該限制的進(jìn)程將會(huì)被終止园蝠。
注意:擴(kuò)展的進(jìn)程內(nèi)存限制很低,某些技術(shù)痢士,如地圖視圖和SpriteKit都需要較高的內(nèi)存成本彪薛,不適合在擴(kuò)展中使用。
- [vm‐pageshortage]/[vm‐thrashing]/[vm]:進(jìn)程由于內(nèi)存壓力而被殺死良瞧。
- [vnode‐limit]:打開了太多的文件陪汽。
注意:最前面的應(yīng)用即使耗盡所有虛擬節(jié)點(diǎn),系統(tǒng)也不會(huì)將其殺死褥蚯。這意味著你的應(yīng)用在后臺(tái)時(shí),即使不是過量使用虛擬節(jié)點(diǎn)的源也有可能會(huì)被結(jié)束况增。
- [highwater]: 一個(gè)系統(tǒng)守護(hù)進(jìn)程跨越了它的內(nèi)存使用的最大標(biāo)記值赞庶。
- [jettisoned]: 其它原因?qū)е逻M(jìn)程被拋棄。
如果你沒有看到原因中列出你的應(yīng)用/擴(kuò)展的進(jìn)程,那么可能不是因?yàn)閮?nèi)存壓力引起的崩潰歧强。查看.crash文件(上一節(jié)講述的)了解更多信息澜薄。
當(dāng)你看到一個(gè)低內(nèi)存崩潰時(shí),與其關(guān)心那一部分代碼在應(yīng)用終止時(shí)正在執(zhí)行摊册,倒不如檢查你對(duì)內(nèi)存的使用方式和對(duì)低內(nèi)存警告的響應(yīng)處理肤京。《在你的應(yīng)用中查找內(nèi)存問題》一文中詳細(xì)地講述了如何使用Instruments的Leaks分析來發(fā)現(xiàn)內(nèi)存泄露茅特,以及如何使用Allocations分析的Mark Heap功能來防止出現(xiàn)被遺棄的內(nèi)存忘分。《內(nèi)存使用性能指南》論述了一種正確的方法來應(yīng)對(duì)低內(nèi)存通知白修,同時(shí)又提供了很多有效使用內(nèi)存的技巧妒峦。同時(shí)也建議你看看WWDC2010年會(huì)議,使用Instruments進(jìn)行高級(jí)內(nèi)存分析兵睛。
重點(diǎn):Instruments的Leaks和Allocations分析無法跟蹤所有的內(nèi)存使用肯骇。你需要與Instruments的VM Tracker一起來運(yùn)行你的應(yīng)用(包含在Instruments的Allocations的模版中)來查看所有的內(nèi)存使用量。VM Tracker默認(rèn)是不開啟的祖很,要想啟動(dòng)VM Tracker笛丙,可以通過點(diǎn)擊Instruments,選中“Automatic Snapshotting”選項(xiàng)或者手動(dòng)按下“Snapshot Now”按鈕假颇。
相關(guān)文檔
有關(guān)如何使用Instruments的Zombies模版來修復(fù)內(nèi)存過度釋放而崩潰的更多信息若债,請(qǐng)參考《通過Zombies模版來消除僵尸對(duì)象》
有關(guān)應(yīng)用歸檔的更多信息,請(qǐng)參考《通過XCode的Archive功能來進(jìn)應(yīng)用分發(fā)與測(cè)試》
關(guān)于解析崩潰日志的更多信息拆融,請(qǐng)參考《iPhone OS WWDC 2010會(huì)議上的了解崩潰報(bào)告》
文檔修改記錄
日期 | 備注 |
---|---|
2015‐07‐21 | XCode6開展的崩潰報(bào)告討論更新 |
2012‐12‐13 | 增加更多的異常代碼信息 |
2012‐03‐28 | XCode4更新蠢琳,加入關(guān)于低內(nèi)存崩潰報(bào)告和更多異常代碼信息。 |
2011‐03‐01 | iOS4或更高版本的變化更新 |
2010‐07‐06 | 修復(fù)文檔中的Bug |
2010‐05‐18 | iPhone OS3.2和XCode 3.2.2的變化更新 |
2009‐06‐01 | 添加強(qiáng)調(diào)不僅需要保存.dSYM文件镜豹,還需要保存應(yīng)用的二進(jìn)制文件傲须。 |
2009‐04‐30 | iTunes Connect崩潰日志服務(wù)更新 |
2009‐02‐18 | 包含一個(gè)防止應(yīng)用程序代碼被符號(hào)化的問題解決方案更新 |
2009‐01‐29 | 為開發(fā)人員說明如何符號(hào)化、了解和分析崩潰報(bào)告的新文檔 |
翻譯的處女作趟脂,如果有不對(duì)的地方請(qǐng)狠狠地指出來_泰讽。
原文鏈接:https://developer.apple.com/library/ios/technotes/tn2151/_index.html