2010-01-26 65 views
14

我有一個函數,它需要一些位圖數據並從中返回一個UIImage *。它看起來像這樣:緩衝區> CGImageRef-> UIImage的正確內存管理模式是什麼?

UIImage * makeAnImage() 
{ 
    unsigned char * pixels = malloc(...); 
    // ... 
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 
    CGImageRef imageRef = CGImageCreate(..., provider, ...); 
    UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
    return [image autorelease]; 
} 

任何人都可以解釋到底誰在這裏擁有什麼內存?我想正確清理,但我不確定如何安全地進行清理。 Docs對這些模糊不清。如果我在這個函數的末尾創建了UIImage,然後使用UIImage,我會崩潰。如果我在創建UIImage之後釋放提供程序或imageRef,我沒有看到崩潰,但它們顯然是將像素全部傳遞過來的,所以我很擔心釋放這些中間狀態。 (我知道每CF文件,我應該需要調用後者的發佈,因爲它們來自創建函數,但我可以在使用UIImage之前做到這一點?)大概我可以使用提供程序的dealloc回調來清理像素緩衝區,但還有什麼?

謝謝!

回答

21

這裏的拇指規則是「-release *它,如果你不需要它」。

因爲你不再需要providerimageRef之後,你應該-release所有的人,即

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
CGDataProviderRelease(provider); 
CGImageRelease(imageRef); 
return [image autorelease]; 

pixel不是由裁判計數管理的,所以你需要告訴CG API釋放他們的你必要時。這樣做:

void releasePixels(void *info, const void *data, size_t size) { 
    free((void*)data); 
} 
.... 

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, releasePixels); 

順便說一句,你可以使用+imageWithCGImage:而不是[[[* alloc] initWithCGImage:] autorelease]。更好的是,有+imageWithData:,所以你不必亂搞CG和malloc的東西。

(*:除了當retainCount已經推測從一開始就零)

+0

謝謝Kenny。這是一個很好的簡潔描述;我想我被原始的堆緩衝區的不可預測性所拋棄了一些,但是一如既往地相信規則並且會得到回報。乾杯。 – 2010-01-26 07:10:39

+1

只需添加一些說明,核心功能中的關鍵詞就是「創建」和「新建」。如果函數包含這些單詞中的任何一個,則必須釋放返回的內存。 大多數核心數據類型都是CFType兼容的。這意味着如果它更容易,你可以使用Objective-C保留/釋放/ autorelease調用。即[(id)imageRef釋放];或CFRelease(imageRef); – 2010-01-26 09:06:24

+0

如果使用CFRelease,請記得檢查'imageRef'是否爲NULL。 – kennytm 2010-01-26 09:33:31

-2

是的,這段代碼讓我感到不安。作爲一個古老的規則,我儘量不要在相同的函數/方法/選擇器中混合並匹配C和C++,以及C/Objective-C。

如何將其分解爲兩種方法。把這個makeAnImage改成makeAnImageRef,並把UIImage創建成另一個Obj-C選擇器。

+0

:)這樣做還不行幫助我的記憶管理問題。我仍然有這樣的問題,即將原始字節傳遞到管道的開始處留給我一個UIImage,它仍然需要這些字節。所以這段代碼比看起來更相關。 – 2010-01-26 05:39:32

8
unsigned char * pixels = malloc(...); 

您擁有pixels緩衝區,因爲你mallocked它。

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

核芯顯卡遵循的核心基礎規則。你擁有數據提供者,因爲你Created它。

您沒有提供釋放回調,因此您仍然擁有pixels緩衝區。如果您提供了釋放回調,則CGDataProvider對象將在此取得緩衝區的所有權。 (通常是一個好主意。)

CGImageRef imageRef = CGImageCreate(..., provider, ...); 

您擁有CGImage對象,因爲你創造了它。

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 

您擁有UIImage對象,因爲你allocked它。

您還擁有CGImage對象。如果UIImage對象想要擁有CGImage對象,它將保留它或創建自己的副本。

return [image autorelease]; 

你放棄你的形象所有權。因此,你的代碼會泄漏像素(你沒有將所有權轉讓給數據提供者,而你自己也沒有釋放它們),數據提供者(你沒有發佈它)和CGImage(你沒有發佈它) t釋放它)。固定版本會將像素的所有權轉移給數據提供者,並且在UIImage準備好時將釋放數據提供者和CGImage。或者,只要使用imageWithData:,正如KennyTM所建議的那樣。

+0

彼得:感謝您抽出時間來解決這個問題,特別是問答聚會結束後。 – 2010-01-27 16:17:48

1
unsigned char * pixels = malloc(...); 

我也有使用malloc /免費使用問題CGImageCreate 我終於找到好的和簡單的解決方案之後。 我只是更換行:

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

有:

剛過
NSData *data = [NSData dataWithBytes:pixels length:pixelBufferSize]; 
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); 

,我可以釋放mallocked內存:雖然我理解的反應

free (pixels); 
+1

這需要複製所有的字節,這對某些用例可能是低效的。相反,使用[NSData dataWithBytesNoCopy:length]將取得字節的所有權,並在不再需要時將它們釋放。 – 2015-04-12 00:06:41