1. 程式人生 > >OC--記憶體管理之自動釋放,[NSMutableArray array]生成的自動釋放陣列被自動釋放後引起的嚴重後果

OC--記憶體管理之自動釋放,[NSMutableArray array]生成的自動釋放陣列被自動釋放後引起的嚴重後果

在做一個TableView程式時,要在表格裡顯示一個資料夾內所有檔案的清單,程式在一開始顯示時正常,但是一滾動視窗時就崩潰,查詢這個錯誤整整花了我一天的時間,原來出在NSMutableArray初始化時用的方法不正確,都是因為Objective-C的基礎知識沒學好。

在一個UITableViewController類中聲明瞭一個NSMutableArray *localFiles這樣一個數組,作為成員變數。

在viewDidLoad方法中讀出應用程式Documents目錄下的所有檔名放在這個陣列中,初始化時用了這個方法:

localFiles = [NSMutableArray array];    //這是自動釋放的物件

然後迴圈呼叫了addObject方法,其它就沒有什麼特殊的操作了,程式能夠顯示出一個檔案列表,但向上或向下滾動一點點時,程式就崩潰,顯示的錯誤資訊也相當奇怪:

[__NSArrayI addObject:]: unrecognized selector sent to instance 0x4b1b8b0

我查啊查啊,每次出錯的呼叫棧都顯示在執行到cellForRowAtIndexPath這個方法時出錯,跟蹤到localFiles這個變數時,在偵錯程式上顯示out of scope,這個localFiles指標裡的內容不知道跑到哪裡去了。

從google上查了許多資料,在這個網站的留言中查到下面一段話,頓時茅塞頓開:

It doesn't really matter. [NSMutableArray array] is a nice shortcut, but you have to remember to retain it, so the question really is a matter of [[NSMutableArray array] retain] versus [[NSMutableArray alloc] init]. I usually use the former. The real convenience comes in when you need to statically fill the array; you can do it all in one message. [[

NSMutableArray arrayWithObjects:...] retain] is faster than [[NSMutableArray alloc] init] followed by numerous [NSMutableArray addObject:(id)] calls.

原來在呼叫array方法後得retain!或者改用[[NSMutableArray alloc] init]方法組合,試了2種修改辦法果然都好用,程式再也不崩潰了。就這麼幾個字,害了我幾乎一整天。

localFiles = [[NSMutableArray array] retain];

這一天裡惡補了一些Objective-C中的記憶體管理知識,雖然走了一些彎路,但對AutoRelease這個術語算是有了一些認識了。

後來仔細看了《Objective C教程》的第9章“記憶體管理”,原來第9.3節“Cocoa記憶體管理規則”(第138頁)裡作者早就強調三條規則:

(1)當你使用new、alloc或copy方法建立一個物件時,該物件的引用計數器值為1。當不再使用該物件時,你要負責向該物件傳送一條release或autorelease訊息。這樣,該物件將在其使用壽命結束時被銷燬。

(2)當你通過任何其它方法獲得一個物件時,則假設該物件的引用計數器值為1,而且已經被設定為自動釋放,你不需要執行任何操作來確保該物件被清理。如果你打算在一段時間內擁有該物件,則需要保留(retain)它並確保在操作完成時釋放它。

(3)如果你保留(retain)了某個物件,你需要(最終)釋放或自動釋放該物件。必須保持retain方法和release方法的使用次數相等。

我的程式是iPhone程式,為了降低程式的記憶體空間佔用,Cocoa在GUI應用程式中規定了自動釋放池的銷燬時間,在程式開始處理事件之前建立一個自動釋放池,並在事件處理結束後銷燬該自動釋放池。我的localFiles物件在開始時建立了一個自動釋放物件,完成之後進行下一個事件迴圈,自動釋放池已經銷燬,localFiles物件也就被釋放了,在滾動tableView時進入下一個事件迴圈,所以localFiles物件的內容就找不到了。