2011-03-07 81 views
1

我的應用程序在動畫中使用大量縮放圖像。爲了避免跳幀,我縮放我的圖像並保存它們,然後運行動畫。這裏是我的代碼保存圖像:使用UIImagePNGRepresentation保存圖像時的內存問題

+ (void)saveImage:(UIImage *)image withName:(NSString *)name { 
    NSData *data = UIImagePNGRepresentation(image); 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 
    NSString *directory = [paths objectAtIndex:0]; 
    NSString *fullPath = [directory stringByAppendingPathComponent:name]; 
    [fileManager createFileAtPath:fullPath contents:data attributes:nil]; 

} 

不幸的是,當我反覆調用這個函數,我有內存問題。我想我試圖保存大約10MB的圖像。我在想,也許問題在於自動發佈的變量 - 也許我應該分配數據,並在最後發佈。但我找不到UIImagePNGRepresentation的分配版本。誰能幫忙?

+0

收到來自@occulus和@Tommy wrt的好答案。 autoreleased的變種 - 但似乎並沒有解決問題,我想知道是否有其他事情可以成爲原因!? – iPadDeveloper2011 2011-03-07 03:37:09

+0

我想我現在明白了這個問題。我需要有一個autorelease池,當我_scale_圖像,以及當我保存它們。 – iPadDeveloper2011 2011-03-07 04:27:11

回答

1

外循環是什麼樣的?如果是這樣的:

for(n = 0; n < 1000; n++) 
{ 
    ... something ... 
    [class saveImage:image withName:name]; 
} 

然後在autorelease池留下的東西可能是你的問題。只有當調用堆棧完全退回到運行循環時,autorelease池纔會被釋放(否則你將無法使用自動釋放的東西作爲返回結果)。既然你不釋放任何東西,你可以嘗試修改您的代碼:

+ (void)saveImage:(UIImage *)image withName:(NSString *)name { 
    // create a local autorelease pool; this one will retain objects only 
    // until we drain it 
    NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; 

    NSData *data = UIImagePNGRepresentation(image); 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 
    NSString *directory = [paths objectAtIndex:0]; 
    NSString *fullPath = [directory stringByAppendingPathComponent:name]; 
    [fileManager createFileAtPath:fullPath contents:data attributes:nil]; 

    // drain the pool, which acts like release in reference counted environments 
    // but also has an effect in garbage collected environments 
    [localPool drain]; 
} 

所以,對於每一個保存你創建自己的自動釋放池的圖像。最近初始化的自動釋放池會自動設置自己從那時起捕獲所有自動釋放的對象。在像iOS這樣的垃圾收集環境中,調用'drain'會導致它被釋放,並立即釋放它所有的對象。

+0

嘿,謝謝@Tommy。我剛剛嘗試過,但得到了「程序接收信號:」EXC_BAD_ACCESS「。」錯誤馬上消失。我認爲釋放數據可能有問題,並有相關的圖像!? – iPadDeveloper2011 2011-03-07 00:50:48

+0

好吧,編譯並再次運行這個,沒有任何變化,並且這次的工作很好 – iPadDeveloper2011 2011-03-07 00:57:06

+0

不幸的是,我現在無法在設備上測試這個,因爲我得到這個瘋狂的「無法註冊...與引導程序服務器。錯誤:未知的錯誤代碼 這通常意味着此過程的另一個實例已經運行或掛在調試器中。程序接收信號:「SIGABRT」。「錯誤。 – iPadDeveloper2011 2011-03-07 00:59:09

4

UIImagePNGRepresentation返回一個自動釋放的NSData對象。換句話說,分配的數據只有在你到達最近的NSAutoreleasePool塊的release(或drain)調用時纔會被處理。

如果您從循環內調用上述代碼,那麼您的代碼可能永遠無法自動釋放所有內存。在這種情況下,您可以將您的呼叫附在您自己的NSAutoreleasePool中:

for (int i = 0; i < 10; i++) { 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    [self saveImage:someImage withName:@"someName.png"]; 

    [pool drain]; 
} 

N.B.我相信用這種方式處理PNG相比使用JPG(UIImageJPGRepresentation)相當慢。只是FYI。

+0

擊敗我12秒! +1。 – Tommy 2011-03-07 00:36:29

+0

我知道了,接近湯米! :) – occulus 2011-03-07 00:41:14

+0

感謝您的快速答案堆!我會嘗試。我正在使用PNG,部分原因是因爲我需要一個透明度組件,部分原因是因爲我明白iPhones就像'PNGs'。 – iPadDeveloper2011 2011-03-07 00:44:33