處理1000張圖片的內存優化

代碼下載:
http://github.com/AlanQuatermain/aqtoolkit/tree/master

如有需要的請自取,包括:
- 寫Log
- 低內存佔用 XML Parser優化
- 加密解密文件
- NSData+Base64
- NSError+CFStreamError
- NSObject+Properties
- NSString+PropertyKVC
- FSEvents 接收文件處理事件
- 實現CFHTTPMessageRef的HTTPMessage wrapper
- 低內存佔用下載
- 流式XML Parser
- 臨時文件處理

一、項目需求

在實際項目中,用戶在上傳圖片時,有時會一次性上傳大量的圖片。在上傳圖片前,我們要進行一系列操作,比如:旋轉圖片爲正確方向,壓縮圖片等,這些操作需要將圖片加載到內存中,下面對內存的使用做詳細分析.


二、內存分析,非優化

我在測試項目中,重複加載了一張圖片1000次,首先加載圖片到內存,然後進行壓縮操作,釋放內存

 

for (int i = 0; i <= 1000; i ++) {

 

       //1.首先我們獲取到需要處理的圖片資源的路徑

 

        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];

 

        //2.將圖片加載到內存中,我們使用了alloc關鍵字,在使用完後,可以手動快速釋放掉內存

 

        UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];

 

       //3.這一步我們將圖片進行了壓縮,並得到一個autorelease類型實例

 

        UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];

 

       //4.釋放掉2步驟的內存

 

        [image release];

 

    }

上面的代碼看起來沒有任何問題,可以說是一種標準的代碼寫法,在每一步驟中都對內存做了小心的處理,我們來看一下,實際的內存使用情況:

 

在上圖中可以看到,我們的操作在沒有任何問題的情況下,在加載大量圖片時,還是會造成內存的劇減


可以看到自動釋放內存時,圖片佔用的內存並沒有立即釋放掉


這些資源沒有立即釋放的資源,佔用了寶貴的內存資源,最終使程序被kill


三優化後的內存使用

上面程序被kill,是因爲程序的內存使用問題,在上面的代碼中,我們每一步都對內存做了非常小心的處理,但是在加載大量的圖片時,還是會出現問題。其根本原因就是autorelease惹的禍,autorelease自動釋放內存,並不會立即把內存釋放掉,而是要等到下一個事件週期纔會釋放掉。問題是一些資源我們不得不使用autorelease類型,比如作爲函數的返回值,而且系統api及項目是的大部分也都是這麼做的,如果全都依靠我們手動釋放很容易造成內存泄漏。

 

for (int i = 0; i <= 1000; i ++) {

 

       //創建一個自動釋放池

 

        NSAutoreleasePool *pool = [NSAutoreleasePool new];

 

        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];

 

        UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];

 

        UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];

 

       [image release];

 

       //將自動釋放池內存釋放,它會同時釋放掉上面代碼中產生的臨時變量image2

 

        [pool drain];

 

    }


優化後的,內存使用情況


 可用內存不再明顯的減少


CGImage及UIImage的數據由原來的220多減少到6-7個


可以看到使用了 NSAutoreleasePool後,加載大量圖片的時候內存也不會出現問題

四、自動釋放池概述

(1)自動釋放池被置於一個堆棧中,雖然它們通常被稱爲被“嵌套”的。當您創建一個新的自動釋放池時,它被添加到堆棧的頂部。當自動釋放池被回收時,它們從堆棧中被刪除。當一個對象收到送autorelease消息時,它被添加到當前線程的目前處於棧頂的自動釋放池中。你不能向自動釋放池發送autorelease或retain消息Application Kit會在一個事件週期(或事件循環迭代)的開端—比如鼠標按下事件—自動創建一個自動釋放池,並且在事件週期的結尾釋放它,因此您的代碼通常不必關心。 有三種情況您應該使用您自己的自動釋放池:

  • 如果您正在編寫一個不是基於Application Kit的程序,比如命令行工具,則沒有對自動釋放池的內置支持;您必須自己創建它們。

  • 如果您生成了一個從屬線程,則一旦該線程開始執行,您必須立即創建您自己的自動釋放池;否則,您將會泄漏對象。

  •  如果您編寫了一個循環,其中創建了許多臨時對象,您可以在循環內部創建一個自動釋放池,以便在下次迭代之前銷燬這些
     對象。這可以幫助減少應用程序的最大內存佔用量。

(2) release和drain之間的差異

      在引用計數環境下,release和drain一樣,會直接自動釋放池l對象。

      在GC(垃圾回收)環境下,release是一個no-op(空操作),drain會觸發垃圾回收(如果自上次垃圾回收以來分配的內存大於當前的閾值)。

      通常情況下,您都應該使用drain而不是使用release來銷燬自動釋放池。

     -drain方法只適用於Mac OS X10.4(Tiger)及更高版本。

     在OS X Mountain Lion v10.8操作系統下,GC(垃圾回收)將被廢棄,ARC(Automatic Reference Counting自動引用計數)爲推薦的替代技術。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章