2010-01-26 55 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