一、項目需求
實際項目中,用戶在上傳圖片時,可能會一次性上傳大量的圖片。上傳圖片前,我們要進行一系列操作,比如:旋轉圖片為正確方向,壓縮圖片等,但是這些操作需要將圖片加載到內存中,下面對內存的使用做詳細分析.
二、內存分析,沒有優化
我在項目中,重復加載了一張圖片1000次,首先加載圖片到內存,然后進行壓縮操作,釋放內存
上述的代碼看起來沒有任何問題,是一種標準的代碼寫法,每一步驟中都對內存做了小心的處理,但是,實際的內存使用情況:
上圖中可以看到,上述的操作在沒有任何問題的情況下,加載大量圖片時,還是會造成內存的飆升。
可以看到自動釋放內存時,圖片占用的內存并沒有立即釋放掉
圖片資源沒有被立即釋放,占用了寶貴的內存資源,最終程序被操作系統殺死。
三、 優化后的內存使用
上面程序被操作系統殺死,是因為程序的內存使用問題,在上面的代碼中,我們每一步都對內存做了非常小心的處理,但是在加載大量的圖片時,還是會出現問題。其根本原因就是autorelease的問題,autorelease自動釋放內存,但是并不會立即把內存釋放掉,而是需 要等到下一個事件周期才會釋放掉。問題是一些資源我們不得不使用autorelease類型,比如作為函數的返回值,而且系統api及項目是的大部分也都是這么做的,如果全都依靠我們手動釋放很容易造成內存泄漏。
優化后的,內存的實際使用情況。
可用內存不再急劇下降。
CGImage及UIImage的數量由原來的220多減少到7個。
可以看到使用了NSAutoreleasePool后,加載大量圖片的時候內存也不會出現問題。
四、自動釋放池概述
1. 自動釋放池被置于一個堆棧中,雖然它們通常被稱為被“嵌套”的。創建一個新的自動釋放池時,它被添加到堆棧的頂部。當自動釋放池被回收時,它從堆棧中被刪除。當一個對象收到系統的autorelease消息時,它被添加到當前線程的目前處于棧頂的自動釋放池中。程序員不能向自動釋放池發送autorelease或retain消息。Application Kit會在一個事件周期(或事件循環迭代)的開端—比如點擊屏幕事件—自動創建一個自動釋放池,并且在事件周期的結尾釋放它,因此代碼通常不必關心釋放問題。但是以下三種情況需要自己創建的自動釋放池:
一個不是基于Application Kit的程序,比如命令行工具,則沒有對自動釋放池的內置支持;需要自己創建它們。
一個異步線程,一旦該線程開始執行,需要立即創建自動釋放池;否則,將會泄漏對象。
一個循環,其中創建了許多臨時對象,可以在循環內部創建一個自動釋放池,以便在下次迭代之前銷毀這些臨時對象。自動釋放池可以幫助減少應用程序的最大內存占用量。
2. release和drain之間的差異
在引用計數環境下,release和drain一樣,會直接自動釋放對象。
在GC(垃圾回收)環境下,release是一個no-op(空操作),drain會觸發垃圾回收(如果自上次垃圾回收以來分配的內存大于當前的閾值)。
通常情況下,需要使用drain而不是使用release銷毀自動釋放池。
-drain方法只適用于Mac OS X10.4(Tiger)及更高版本。