網頁

2012年11月21日 星期三

iOS分享_如何使用Xcode內建工具找到記憶體洩漏(Memory Leak)

不管你是自修或是到外面上課,書本或是講師一定會告訴你“管理記憶體”非常的重要,而記憶體管理對於有C語言底子的人可能非常的自然,但是對於其他程式語言起家的程式設計師(諸如我),倒是一件幾乎完全從零開始的事情;因為知道要管理,我們會在撰寫iOS時自以為地使用alloc分配記憶體,再在自覺得適當的時刻利用release將其釋放掉,當我們完成一個階段並且開始在模擬器或實機跑時,發現程式正常運作,那麼我們可能自我感覺良好的認為完成了一個很OK的程式。

但是其實有時候可能在某些呼叫的方法上,iOS會自己管理記憶體,並不需要你去觸碰,也有可能在不同function間傳遞時因為操作有誤,導致每次呼叫便使得retaincount不斷上升,而記憶體管理需要不斷地學習,瞭解需要管理與釋放的變數與釋放的時機。

本篇並沒有要提及如何寫出良好記憶體管理的方法,我也沒有能力寫出這樣偉大的文章,但即使我們沒有辦法一次就達到完善的記憶體管理,卻可以經過一些工具一步步向前。

截至目前為止,我所知道要尋找Memory Leak的方法有兩個,你應該針對你的程式先完成第一個測試,再進一步完成第二個,直至程式修改成你所接受的記憶體管理:

1. 使用Xcode的Analyze功能。

這個功能可以幫助你去尋找程式碼中,有建立記憶體空間卻沒有釋放的位置,我們將一個release註解來看看他的結果:

接著你可以對他點擊,以獲取更多的資訊。

這一個測試僅能以編譯的手段,去尋找那些你可能忘記釋放的地方,也就是你必須要釋放的那些變數,所以最基本的要求就是讓該測試沒有任何提醒。但即便該測試完成,也不代表該程式在執行階段不會發生Memory Leak的問題,原因是有可能程式需要上傳相片,那麼我們會使用[NSData dataWithContentsOfFile:filePath];的方式將檔案轉為NSData,再傳送出去,而在從檔案轉成NSData到上傳的過程中,會使用非常多變數與方法,其中一個該釋放但未釋放,則可能造成暫存該檔案的空間一直存在,所以當你不斷進行上傳的動作,會發現你的程式似乎越來越慢?直到Crash為止。

所以我們必須藉由第二個工具來輔助我們尋找程式運行中可能的記憶體洩漏。

2. 使用Xcode的Profile工具。

點擊後會新開一個名叫Instruments的程式,選Leaks後點擊Profile。

你的程式會開始運作,並且跑出一些好像在監測什麼的圖表?

接著你可以試著操作程式,會發現該圖表不斷的跳動,

而這個名為Allocations就是程式記憶體的分配,你可以藉由觀察下方表格的All Allocations*來得知目前的總使用量,若是你不斷地在頁面間來回反覆操作一樣的動作,而該記憶體用量只增不減,就代表在這個動作的過程中,應該有一些需要調整的地方。

當然,我們不能僅藉由這樣的方式來找尋應該要修改的地方,點選左邊的Leaks區域,會進到Leaks的監測畫面:

接著在繪圖區與表格區中間,有一個“田”Leaks,點選後選擇Call Tree:
最後將左側的Hide System Libraries與Invert Call Tree打勾(Hide System Libraries幫助你只檢視自己程式的問題,Invert Call Tree是將檢視的過程顛倒,讓你馬上可以點到出問題的最後一段程式碼),讓我們製造一些Leak的問題,可以看到該工具會將Leak列表:

雙擊列表兩下則可以得知“可能”出錯的位置:

最後,你可以嘗試的修復這些洩漏,直到程式運作過程完全沒有為止。

講起來有工具而且已經幫助你找到相關位置,理論上應該很容易解決這些問題?但實際上這些Memory Leak的問題通常會牽扯到不同函式間的呼叫,洩漏的位置往往不是問題的原因。連續一個星期都在處理Leak相關問題,這實在比平常在解決其他程式語言的問題時更加令人惱羞,更深深感覺每一個問題的解決背後真的需要很多知識的累積,近一兩年常有人問我要學什麼語言?學什麼最好?某語言好學嗎?我的答案總是讓人不以為然:

你有心學就可以學起來,實際打開電腦寫遠比一直想著要不要學有用多了,而每一種語言的特性都有可能讓你更瞭解別種語言,如果你要學的是if跟for,那什麼都可以學,我第一個學的是MATLAB。

沒有留言:

張貼留言