ios 調試技巧收藏 一 解決EXC_BAD_ACCESS錯誤的一種方法--NSZombieEnabled

我們做iOS 程序開發時經常用遇到 EXC_BAD_ACCESS 錯誤導致 Crash,出現這種錯誤時一般 Xcode 不會給我們太多的信息來定位錯誤來源,只是在應用 Delegate 上留下像Thread 1: Program received signal:"EXC_BAD_ACCESS",讓問題無從找起。

比如你對已釋放的對象發送消息時就會出現,EXC_BAD_ACCESS,再如release 的對象再 release,release 那些autorelease 的對象等也會報這樣的錯。默認設置下 Xcode 不會給你定位具體是哪一行代碼,不該去使用已釋放的對象,或者release 用錯了。

比如 UIViewController 子類中這樣的代碼:

  1. static NSMutableArray*array;  
  2.    
  3. -(void)viewDidLoad  
  4. {  
  5.     [superviewDidLoad];  
  6.     array= [[NSMutableArray alloc]initWithCapacity:5];  
  7.     [array release];//釋放掉該數組  
  8. }  
  9.    
  10. - (void)viewWillAppear:(BOOL)animated{     
  11.     [array addObject:@"Hello"];//使用釋放掉的數組  
  12. }  

上面的代碼就會出現EXC_BAD_ACCESS 錯誤,但我執行時 Xcode 一出錯卻是定位在我在 AppDelegate 的 application:didFinishLaunchingWithOptions: 方法上的某行了,如果代碼量多了,要查找具體問題非常難,但憑經驗了。

不過NSZombieEnabled 環境變量可以幫我們的忙,就是當設置NSZombieEnabled環境變量後,一個對象銷燬時會被轉化爲_NSZombie,設置NSZombieEnabled後,當你向一個已經釋放的對象發送消息,這個對象就不會向之前那樣Crash或者產生一個難以理解的行爲,而是放出一個錯誤消息,然後以一種可預測的可以產生debug斷點的方式消失, 因此我們就可以找到具體或者大概是哪個對象被錯誤的釋放了。 

對 Xcode 設置了NSZombieEnabled 之後,Xcode 會明確定位在行[array addObject:@"Hello"],然後控制檯下報的錯誤信息是:

*** -[__NSArray addObject:]:message sent to deallocated instance 0x6557370

如何設置 NSZombieEnabled 呢,在 Xcode3 和 Xcode4 下設置不一樣,Xcode4 下設置很簡單。
Xcode3 下 NSZombieEnabled 設置方法如下:

1.   在XCode左邊那個Groups& Files欄中找到Executables,雙擊其中的一項,或者右鍵Get Info;
2.  切換到Arguments 
3.  這裏一共有兩個框,在下面那個Variables to be set in theenvironment:點+號添加一項,Name裏填NSZombieEnabled,Value填Yes,要保證前面的鉤是選中的。

Xcode4 下設置 NSZombieEnabled 的方法:

你可以點擊 Xcode4 菜單 Product -> Edit Scheme-> Arguments, 然後將點擊”加號”, 將 NSZombieEnabled 參數加到Environment Variables 窗口中, 後面的數值寫上 ”YES”.

或者在 Xcode4 菜單 Product -> EditScheme -> Diagnostics 設置窗口中直接勾上Enable ZombieObjects 即可,Xcode 可用 cmd+shift+< 進到這個窗口。

NSZombieEnabled

Xcode4 已經考慮到了現在的要求,所以提供了更便捷的設置的方式,你也可以在這個窗口中設置其他一些參數,你肯定能由此獲得更多的幫助信息。

另外再說一下,如果沒有爲 Xcode 設置 NSZombieEnable,像下面的代碼或許可以正確執行,打印出你所期望的結果“Hello”

  1. static NSMutableArray*array;  
  2.    
  3. -(void)viewDidLoad  
  4. {  
  5.     [super viewDidLoad];  
  6.     array= [[NSMutableArray alloc]initWithCapacity:5];  
  7.     [array release];  
  8.     [array addObject:@"Hello"];//之所以不會crash,是在於事件週期未完,內存回收機制還沒有執行,沒有真正的回收掉array的對象內存。  
  9.     NSLog(@"%@",[array objectAtIndex:0]);  
  10. }  


但是一旦加上了NSZombieEnable 設置,上面的代碼行  [array addObject:@"Hello"] 也將無法投機取巧了,同樣會得到錯誤提示:

*** -[__NSArrayM addObject:]:message sent to deallocated instance 0x6557370

即使該array 所指向的內存還是原來的數據也不能逃脫掉 NSZombieEnable 的法眼。也就是之所以未設置 NSZombieEnable 時上面代碼能得到正確結果,是因爲,雖然 [array release] 是標記爲釋放掉該內存塊,但是後面使用 array 時,因爲該指針指向的內存數據未被覆蓋,所以未出錯,這和C++ 的指針 delete 後的效果是一樣的。


  1. CocoaDev,個人覺得講Cocoa技術十分專業的網站之一,下面的鏈接詳細講了講NSZombieEnable的原理。http://www.cocoadev.com/index.pl?NSZombieEnabled
  2. 蘋果官方的Mac OS X Debugging Magic,詳細講述了最爲一個高級蘋果程序員應該具備的調試技巧 http://developer.apple.com/library/mac/#technotes/tn2004/tn2124.html
  3. 其實還可以在Instruments中開啓NSZombie選項,這樣就可以在Instruments中直接查看crash時候的callstack了:http://www.markj.net/iphone-memory-debug-nszombie/

最後提醒NSZombieEnabled只能在調試的時候使用,千萬不要忘記在產品發佈的時候去掉,因爲NSZombieEnabled不會真正去釋放dealloc對象的內存,一直開啓後果可想而知,自重!

本文鏈接 http://unmi.cc/nszombieenabled-locate-exc_bad_access-error,來自 隔葉黃鶯 Unmi Blog

發佈了71 篇原創文章 · 獲贊 1 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章