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 子類中這樣的代碼:
- static NSMutableArray*array;
- -(void)viewDidLoad
- {
- [superviewDidLoad];
- array= [[NSMutableArray alloc]initWithCapacity:5];
- [array release];//釋放掉該數組
- }
- - (void)viewWillAppear:(BOOL)animated{
- [array addObject:@"Hello"];//使用釋放掉的數組
- }
上面的代碼就會出現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+< 進到這個窗口。
Xcode4 已經考慮到了現在的要求,所以提供了更便捷的設置的方式,你也可以在這個窗口中設置其他一些參數,你肯定能由此獲得更多的幫助信息。
另外再說一下,如果沒有為 Xcode 設置 NSZombieEnable,像下面的代碼或許可以正確執行,打印出你所期望的結果“Hello”
===================
iOS5之后蘋果公司引入了ARC機制,大大方便了ios開發者對內存的管理機制。在iphone 4出世的時候為什么ios在512M的內存中可以運行很大的游戲,保持暢快流暢的狀態。得益於ios非常好的內存處理機制。
在我們現在創建項目的時候,默認會直接引入ARC機制,我們可以關閉ARC機制:在輸入框中輸入long點擊搜索按鈕,如圖:
接下來即可進行老版本的內存操作了。
在老版本中,內存操作采用了引用計數(retainCount)alloc retain(+1)release(-1)
內存管理原則(配對原則):只要出現了new,alloc,retain,就一定配對出現一個release,autorelease
使引入數+1,必須對應的-1,務必成對存在
ClassA *obj1 = [[ClassA alloc] init]; //retaincount = 1
ClassA *obj2 = obj1; //retaincount = 1
[obj2 retain]; //retaincount = 2
當引入計數變為0的時候,會自動調用系統的dealloc系統函數
-(void)dealloc
{
[super dealloc];//注意一定要調用父類函數
NSLog(@"對象被刪除");
}
野指針:
在開發中經常會遇到野指針,系統一般會提示為Thread 1:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)錯誤。因為你訪問了一塊已經不屬於你的內存。
但我們在開發中可能會發現系統並不會時時檢測野指針,主要是為了提高編譯效率,默認關閉了野指針檢測機制,打開方法:
在Enable Zombie Objects上點擊對號,關閉即可。
注意:一般我們最好不要打開該機制,這樣編譯時的效率將大大降低。
內存泄露:
只要對象的retainCount !=0就會一直存在在內存中,不再被使用的對象,一直在內存中沒有被銷毀,則會造成內存泄露。
@property 參數
1.與set方法內存管理相關參數
retain:要生成符合內存管理原則的set方法(應用與對象類型)
assign:直接賦值(對象類型,基本數據類型)
capy
2多線程相關
nonatomic:不生成多線程代碼。(一般都用這個,效率高)
atomic:生成多線程管理代碼
3.是否要生成set跟get方法
readwrite:可讀可寫屬性,同時生成set跟get方法
readonly:只讀屬性,只生成get方法
4.set與get方法名稱相關的參數
setter:設置生成set方法的名稱
getter:設置生成的get方法名稱
實例:
@property(nonatomic,retain)Class *class;
@property(nonatomic,retain)NSString *name;
@property(nonatomic,assign)int age;
@property(nonatomic,assign,readonly)int age;
@property(nonatomic,assign,setter=abc:)int age;//注意有冒號
- static NSMutableArray*array;
- -(void)viewDidLoad
- {
- [super viewDidLoad];
- array= [[NSMutableArray alloc]initWithCapacity:5];
- [array release];
- [array addObject:@"Hello"];//之所以不會crash,是在於事件周期未完,內存回收機制還沒有執行,沒有真正的回收掉array的對象內存。
- NSLog(@"%@",[array objectAtIndex:0]);
- }
但是一旦加上了NSZombieEnable 設置,上面的代碼行 [array addObject:@"Hello"] 也將無法投機取巧了,同樣會得到錯誤提示:
*** -[__NSArrayM addObject:]:message sent to deallocated instance 0x6557370
即使該array 所指向的內存還是原來的數據也不能逃脫掉 NSZombieEnable 的法眼。也就是之所以未設置 NSZombieEnable 時上面代碼能得到正確結果,是因為,雖然 [array release] 是標記為釋放掉該內存塊,但是后面使用 array 時,因為該指針指向的內存數據未被覆蓋,所以未出錯,這和C++ 的指針 delete 后的效果是一樣的。
- CocoaDev,個人覺得講Cocoa技術十分專業的網站之一,下面的鏈接詳細講了講NSZombieEnable的原理。http://www.cocoadev.com/index.pl?NSZombieEnabled
- 蘋果官方的Mac OS X Debugging Magic,詳細講述了最為一個高級蘋果程序員應該具備的調試技巧 http://developer.apple.com/library/mac/#technotes/tn2004/tn2124.html
- 其實還可以在Instruments中開啟NSZombie選項,這樣就可以在Instruments中直接查看crash時候的callstack了:http://www.markj.net/iphone-memory-debug-nszombie/
最后提醒NSZombieEnabled只能在調試的時候使用,千萬不要忘記在產品發布的時候去掉,因為NSZombieEnabled不會真正去釋放dealloc對象的內存,一直開啟后果可想而知,自重!