EXC_BAD_ACCESS簡單理解
當你遇到由EXC_BAD_ACCESS造成的崩潰時,那就意味著你向一個已經釋放的對象發送消息。這是最常見的情況。
EXC_BAD_ACCESS的本質
在C和Objective-C中,你一直在處理指針。指針無非是存儲另一個變量的內存地址的變量。當您向一個對象發送消息時,指向該對象的指針將會被引用。這意味著,你獲取了指針所指的內存地址,并訪問該存儲區域的值。
當該存儲器區域不再映射到您的應用時,或者換句話說,該內存區域在你認為使用的時候卻沒有使用,該內存區域是無法訪問的。 這時內核會拋出一個異常( EXC ),表明你的應用程序不能訪問該存儲器區域(BAD ACCESS) 。
簡而言之,當你碰到EXC_BAD_ACCESS ,這意味著你試圖發送消息到的內存塊,但內存塊無法執行該消息。但是,在某些情況下, EXC_BAD_ACCESS是由被損壞的指針引起的。每當你的應用程序嘗試引用損壞的指針,一個異常就會被內核拋出。
EXC_BAD_ACCESS調試須知的三點
1、調試EXC_BAD_ACCESS可能會非常棘手和令人沮喪。
2、你需要知道的是您的應用程序并不一定是在崩潰的那一刻,無法訪問內存區域。這就是常使調試EXC_BAD_ACCESS變得困難的原因。
3、同樣受損指針也是如此。當你的指針被損壞時,您的應用程序不會崩潰。同時,如果您在應用程序中來回傳遞一個受損的指針也不會崩潰。當應用程序試圖引用受損指針的時候,就會發生奔潰。
EXC_BAD_ACCESS調試——僵尸調試模式
在Xcode中,您可以啟用僵尸對象,這意味著被釋放的對象將會以僵尸的形式被保留。換言之,保留釋放的對象就是為了調試。這里沒有涉及任何魔法。如果您向僵尸對象發送消息,你的應用程序將會由于EXC_BAD_ACCESS而崩潰。
這有什么好處嗎?讓EXC_BAD_ACCESS難以調試的原因是,你不知道你的應用程序試圖訪問哪個對象。僵尸對象在許多情況下解決這個問題。通過保留已釋放的對象,Xcode可以告訴你你試圖訪問哪個對象,這使的查找問題原因容易得多。
如何調試僵尸對象
經過上面的真機調試之后,發現我們的程序崩在了一個方法里,并且報錯 “Thread 1:EXC_BAD_ACCESS(code=1,address=0x4000)”,這種錯誤通常是內存管理的問題,一般是訪問了已經釋放的對象導致的,可以開啟僵尸對象(Zombie Objects)來定位問題。
第一步:還是打開Xcode 選擇屏幕左上角Xcode-> PReferencese,不過我們這次是要設置一下輸出信息,調試的時候輸出更多的信息,如下截圖,勾上:
第二步:再對環境變量進行設置:菜單Product > Scheme > Edit Scheme
把紅色圈里面的三個選項都勾上
開啟該選項后,程序在運行時,如果訪問了已經釋放的對象,則會給出較準確的定位信息,可以幫助確定問題所在。
該功能的原理是,在對象釋放(retainCount為0)時,使用一個內置的Zombie對象,替代原來被釋放的對象。無論向該對象發送什么消息(函數調用),都會觸發異常,拋出調試信息。
記得在問題被修復后,關閉該功能??!
第三步:設置好后調試程序,在輸出界面發現了-[CFString retain]: message sent to deallocated instance錯誤日志
到這里,就已經很明顯看出來是什么原因導致程序崩潰的了,然后再去分析代碼,靜下心來肯定能解決問題的了。
我這里是因為向一個空的NSString類型發送消息導致崩潰的,但是這個問題只在iOS9版本崩潰,iOS10就沒問題,這個還值得深究。
例子
為了能夠更加詳細地說明調試僵尸對象,并定位到崩潰的原因,下面列出一個簡單的例子來說明:
先創建一個 DebugViewController,然后里面創建一個數組,然后釋放,在Controller將要出現的時候,向該數組發送一個消息:
#import "DebugViewController.h"
@interface DebugViewController ()
@end
@implementation DebugViewController
/*定義一個數組*/
static NSMutableArray*array;
-(void)viewDidLoad
{
[super viewDidLoad];
array= [[NSMutableArray alloc]initWithCapacity:5];
[array release];//釋放掉該數組
}
- (void)viewWillAppear:(BOOL)animated{
[array addObject:@"Hello"];//使用釋放掉的數組
}
@end
在我們意料之中,程序崩潰了,報錯信息如下:
我們用LLDB po我們的數組array對象,同樣沒有返回
po數組對象
打開“活動監視器”,在進程列表中找到測試APP對應的進程號PID(Xcode啟用調試后會在進程列表中找到對應APP的進程)
從上面標下劃線的地方,我們得到兩個主要的信息:
APP進程ID:21122
崩潰地址:0x60008170cfd0
打開“終端”,輸入以下命令:
sudo malloc_history 21122 0x60008170cfd0
得到錯誤日志,這樣就能定位到最后調用的那行代碼
就是我們上面的release釋放掉了array對象導致的。
相關文章參考: