注意到經常有一個這樣的問題:某指針對象先release後=nil,這裡後跟個=nil有什麼作用?不寫行不行?
簡單一點說是,release是用來釋放內存,nil是將對象指針設為null,nil本身對內存沒什麼影響,但他處理指針,尤其是避免野指針倒是很有必要。
舉一個例子:
NSString *str=[[NSString alloc] init];
當我不需要str時
執行[str release];
str 的retain值減1,但是如果當前retain值>0,卻緊隨其後加一句str=nil;那麼這時[str retainCount]應該為0,因為[nil retainCount]==0;但這時很明顯存在內存洩露。
所以一個很好的寫作習慣是:
當對象的retainCount==1時,寫完[str release];後接著寫上str=nil; 這樣,當在後面的代碼再次調用str相關的方法屬性,也不會報錯,因為之前已經將str設置為空指針,再調用str的方法也會被認為是null,不會真正調用,更不會報錯。
例如下列代碼:(以下所有代碼段都已經過實際操作驗證)
[cpp]
- UIView *view1=[[UIView alloc] init];
- UIView *view2=[view1 retain];
- int i=[view1 retainCount];
- NSLog(@"i:%d",i);
- [view1 release];
- view1=nil;
- [view1 addSubview:view2];
整個程序運行,是不會crash的。但是存在內存洩露。
但是,現在又有另外一個問題,請看下列代碼:
[cpp]
- UIView *view1=[[UIView alloc] init];
- UIView *view2=[view1 retain];
- int i=[view1 retainCount];
- NSLog(@"i:%d",i);
- [view1 release];
- view1=nil;
- [view1 addSubview:view2];
- i=[view1 retainCount];
- NSLog(@"i:%d",i);
- i=[view2 retainCount];
- NSLog(@"i:%d",i);
- [view2 release];
- i=[view2 retainCount];
- NSLog(@"i:%d",i);
請問終端輸出的log應該是什麼?
第一個i=2,沒問題,因為view1 init了一次,retain了1次,retain值為2。
第二個i=0,根據上面所講的推斷,也沒問題,因為之前view1=nil,空指針的retainCount值為0。
第三個i=1,也沒什麼問題,因為view2是一個被賦了值的新指針,它不同於指針view1,二者是2個獨立的指針,而且,view2還被賦了值分配了內存地址。
但是,第四個i呢?i=?
答案是i=1。為什麼會這樣?下面是真實輸出結果:
[cpp]
- 2011-12-09 01:17:07.364 ReleaseNildemo[19115:f803] i:2
- 2011-12-09 01:17:07.365 ReleaseNildemo[19115:f803] i:0
- 2011-12-09 01:17:07.366 ReleaseNildemo[19115:f803] i:1
- 2011-12-09 01:17:07.367 ReleaseNildemo[19115:f803] i:1
為什麼會這樣?按推理說,程序執行到輸出這句應該crash才對,我運行了好幾遍,確實沒有出現。晚上在網上和其他程序員討論這個問題,有人運行的情況是“有時候會crash,有時候不會。斷點的話不會crash”,最後大家討論的話題開始變成“系統是先輸出還是先回收?”,更有甚者,有人輸出了一下內存地址,發現是view2 release前後輸出的是同一個內存地址,說明,這個東西內存雖然釋放了,但是系統還沒來得及回收。
有時候crash,原因是你向一塊未申請的內存發送了一條消息,最終成了系統回收速度問題了,所以,個人認為,以後碰到類似這種問題,干脆手動init,release,至少這樣能很快釋放,更便於清楚當前對象的內存情況。
以上是我對release nil操作的一些理解,歡迎更多人加入討論中。