高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Set to Nil After Release

image

iOS的記憶體管理真的不少東西要學,在Objective-C裡有時候會看到一些程式碼這樣寫:

1
2
3
4
5
6
7
-(void) someMethod
{
  [mybook release];
  mybook = nil;

  [super dealloc];
}

先把它release掉了,然後下一行再把它指向nil。nil跟其它程式語言裡的null或none是一樣的東西,就是nothing、虛無飄渺、沒有任何東西的意思。

問題是,這個nil是必要的嗎? 不指向nil會出什麼包嗎?

先來釐清一個觀念:對一個物件發送release訊息並不會直接把物件佔用的記憶體還給記憶體,release這個動作只是減少retain count而已,當retain count變成0的時候會自動啟動dealloc,這時候才是真正的被消滅掉。

在Objective-C裡所有的物件變數都是指標,指向某一塊配置的記憶體,所以,下面這行程式碼:

1
mybook = nil;

意思是把mybook這個變數指向nil。這樣說來,是不是也不用什麼release了,直接把變數設定成nil不就好了?

我們先來看個例子:

1
UIImage *img = [[UIImage alloc] init];

這應該不陌生了,這建立一個名為img的指標變數,並指向某一個UIImage的實體。如果我在它下面再加一行:

1
img = [[UIImage alloc] init];

就是再做一次alloc/init,讓剛剛那個img變數指向這個新的實體。看起來很直覺,但指標的東西跟我們一般寫的程式的習慣不同,當你把img指向另一個新的實體,原來舊的實體並不會消失,它會存在記憶體裡,只是沒有人指向它,所以也沒有人能再存取它。當然在有支援GC的環境上,系統會自動回收那些沒人要的東西,但在不支援GC的環境,那個沒人要的孩子就會一個人孤獨的漂流著了,所以如果你要這樣做,請記得先把原來的那顆給release掉,再給它一顆新的。

那如果我再接著這樣做:

1
img = nil;

把img指向nil並不會減少retain count,那剛剛前面我產生的第二個物件就會跟第一個物件一樣,它不會消失,它只是在記憶體空間裡漂流,當然它佔用的記憶體也不會還給系統。所以請切記,如果你要把物件給丟掉,不要直接把變數設成nil,請使用release或autorelease

回到最一開始的問題,我的答案是:其實是沒什麼需要一定要在release之後再弄個nil給它,但這樣做是個好習慣。

那什麼時候會出問題?

在別的程式語言裡,當你要存取null物件的時候,通常會直接跳錯誤給你看,但在Objective-C裡如果你對nil送訊息的,即使是沒定義的method,它也只會靜靜的不回應而已。所以,回到最前面的程式碼來看,當你把mybook給release之後,也許有那個萬一的情況下,有別的method要存取它,但它可能因為已經被dealloc掉了,所以就會爆炸了(發生機會很小,但不代表永遠不會發生)。但如果把mybook指向nil,即使有人要再來存取這個mybook也不會出現錯誤了。

以上,我對記憶體管理的東西的了解相當有限,所以如果以上內容有誤還請不吝指正 :)

Comments