iPhone - UIImage泄漏,CGBitmapContextCreateImage泄漏
好吧我有一个难以追踪这个内存泄漏的世界。运行这个脚本时,我没有看到任何内存泄漏,但我的对象正在爬升。 Instruments指向CGBitmapContextCreateImage> create_bitmap_data_provider> malloc,这占用了我objectalloc的60%。iPhone - UIImage泄漏,CGBitmapContextCreateImage泄漏
该代码被称为NSTimer多次。
//GET IMAGE FROM RESOURCE DIR
NSString * fileLocation = [[NSBundle mainBundle] pathForResource:imgMain ofType:@"jpg"];
NSData * imageData = [NSData dataWithContentsOfFile:fileLocation];
UIImage * blurMe = [UIImage imageWithData:imageData];
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
UIImage * scaledImage = [blurMe _imageScaledToSize:CGSizeMake(blurMe.size.width/dblBlurLevel, blurMe.size.width/dblBlurLevel) interpolationQuality:3.0];
UIImage * labelImage = [scaledImage _imageScaledToSize:blurMe.size interpolationQuality:3.0];
UIImage * imageCopy = [[UIImage alloc] initWithCGImage:labelImage.CGImage];
[pool drain]; // deallocates scaledImage and labelImage
imgView.image = imageCopy;
[imageCopy release];
下面是模糊函数。我相信objectalloc问题位于这里。也许我只需要一双清新的眼睛。如果有人能够弄清楚这一点,那会很棒。对不起,这是很长的...我会尽量缩短它。
@implementation UIImage(Blur)
- (UIImage *)blurredCopy:(int)pixelRadius
{
//VARS
unsigned char *srcData, *destData, *finalData;
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
//IMAGE SIZE
size_t pixelsWide = CGImageGetWidth(self.CGImage);
size_t pixelsHigh = CGImageGetHeight(self.CGImage);
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL) { return NULL; }
bitmapData = malloc(bitmapByteCount);
if (bitmapData == NULL) { CGColorSpaceRelease(colorSpace); }
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
if (context == NULL) { free (bitmapData); }
CGColorSpaceRelease(colorSpace);
free (bitmapData);
if (context == NULL) { return NULL; }
//PREPARE BLUR
size_t width = CGBitmapContextGetWidth(context);
size_t height = CGBitmapContextGetHeight(context);
size_t bpr = CGBitmapContextGetBytesPerRow(context);
size_t bpp = (CGBitmapContextGetBitsPerPixel(context)/8);
CGRect rect = {{0,0},{width,height}};
CGContextDrawImage(context, rect, self.CGImage);
// Now we can get a pointer to the image data associated with the bitmap
// context.
srcData = (unsigned char *)CGBitmapContextGetData (context);
if (srcData != NULL)
{
size_t dataSize = bpr * height;
finalData = malloc(dataSize);
destData = malloc(dataSize);
memcpy(finalData, srcData, dataSize);
memcpy(destData, srcData, dataSize);
int sums[5];
int i, x, y, k;
int gauss_sum=0;
int radius = pixelRadius * 2 + 1;
int *gauss_fact = malloc(radius * sizeof(int));
for (i = 0; i < pixelRadius; i++)
{
.....blah blah blah...
THIS IS JUST LONG CODE THE CREATES INT FIGURES
........blah blah blah......
}
if (gauss_fact) { free(gauss_fact); }
}
size_t bitmapByteCount2 = bpr * height;
//CREATE DATA PROVIDER
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, srcData, bitmapByteCount2, NULL);
//CREATE IMAGE
CGImageRef cgImage = CGImageCreate(
width,
height,
CGBitmapContextGetBitsPerComponent(context),
CGBitmapContextGetBitsPerPixel(context),
CGBitmapContextGetBytesPerRow(context),
CGBitmapContextGetColorSpace(context),
CGBitmapContextGetBitmapInfo(context),
dataProvider,
NULL,
true,
kCGRenderingIntentDefault
);
//RELEASE INFORMATION
CGDataProviderRelease(dataProvider);
CGContextRelease(context);
if (destData) { free(destData); }
if (finalData) { free(finalData); }
if (srcData) { free(srcData); }
UIImage *retUIImage = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
return retUIImage;
}
我能想到的唯一的事情是阻碍了被ObjectAlloc中本的UIImage * retUIImage = [UIImage的imageWithCGImage:cgImage] ...但如何做我释放它已经返回后?希望有人可以帮忙。
我见过类似的行为,并阅读了其他几个类似的帖子。创建一个自动释放池似乎没有帮助。它看起来像库泄漏内存或将其存储在更高级别的自动发布池中,因此它不会被释放,直到太晚。我怀疑在框架中有一个错误,但不能证明它。
它帮助,如果你确实包裹图像的分配,并在池对象存在周期释放,
pool = [[NSAutoreleasePool alloc] init];
[imageView setImage: [UIImage imageNamed:"image1.png"]];
...
[imageView setImage: [UIImage imageNamed:"image2.png"]];
...
[imageView setImage: [UIImage imageNamed:"image3.png"]];
....
[pool drain];
[pool release];
在这个例子中image3.png不会被释放,但图像1和图像2会。
在'-drain'(这相当于非垃圾回收运行时的'-release')之后''不应该'释放',因为这会导致双重版本。 – kennytm 2010-01-14 05:34:11
获取clang并通过它运行您的项目。它不仅会发现你的(静态)泄漏,还会帮助你更多地了解引用计数规则 - 至少它对我来说是这样。
或者如果您正在运行Snow Leopard,请在Xcode 3.2中运行“Build&Analyze”。这是铿锵声,并做出了很好的图表导致问题的执行流程图。 – 2010-03-29 14:55:52
一些想法/想法:
原因之一,这个块的代码显然有问题,因为你可以*的位图数据的两倍,如果上下文无法分配。
if (context == NULL) { free (bitmapData); }
CGColorSpaceRelease(colorSpace);
free (bitmapData);
if (context == NULL) { return NULL; }
我也赞同benzado,你bitmapData,直到你完成上下文......虽然它不是崩溃这是令人费解的。或许幸运。
注意,我敢肯定,数据提供者和位是指将要成为cgImage的一部分返回了CGImageCreate:
提供商 为位图数据的来源。有关支持的数据格式的信息,请参阅下面的讨论。 Quartz保留了这个对象;在返回时,您可以放心地释放它。
所以,这意味着,直到您返回的UIImage被释放,我不认为cgImage或位数据提供程序将会消失。所以,也许问题在于UIImage并不是每个人都会消失?
为什么不使用CGBitmapContextCreateImage()来创建结果图像? (你可能需要调用CGContextFlush()来强制绘制位图,但看起来你正在做所有的像素手动改变,所以可能不需要)。基于模糊的文档,我不认为你应该释放()CGBitmapContextGetData返回的指针(但由于文档的模糊性,这是一个猜测)。
也:
你没有初始化srcData,destData & FinalData的为NULL,以免费的,所以您的测试之前()'荷兰国际集团就显得很危险的。
所有的说(并尝试一些这些东西),因为我们在10.5.6曾在我们的Mac应用程序泄漏在一个点和更早CIImage imageWithCGImage,imageByApplyingTransofrm,imageByCroppingToRect,NSBitmapImageRep initWithCIImage或NSBitmapImageRep CGImage的某些方面泄露。这在10.5.7中得到了修复,因此您正在测试的iPhone OS版本中可能存在类似的情况。
其实如果我记得[UIImage imageNamed]创建自己的缓存,这是你的控制之外。这将解释你所看到的内存使用情况。你可以在这里阅读更多 - http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/。
我已经多次使用Quartz。每次我做的都是噩梦。至于我注意到,我已经填补了漏洞苹果发送一个项目作为碰撞的证据,4件事情之一是真的:
- 石英泄漏的地狱
- 花了很长时间来释放内存
- 它不必要地使用太多的内存或
- 它们的组合。
我曾经创建过一个简单的项目来证明这一点。该项目有一个按钮。每按一次按钮,一个小图像(100x100像素)就被添加到屏幕上。图像由两层组成,图像本身和一个附加层,其中包含围绕图像边框绘制的虚线。这张图是在Quartz中完成的。按下按钮8次使应用程序崩溃。你可以这样说:按下按钮8次,屏幕上添加了16张图片,这就是崩溃的原因,对吧?
我没有使用Quartz绘制边框,而是决定在PNG上预先绘制边框,并将其作为图层添加到对象中。每个对象创建相同的两个图层。对?
我点击了100次,在屏幕上添加了200张图片,没有崩溃。内存永远不会超过800 Kb。我可以继续点击...应用程序继续更快捷。没有记忆警告,没有崩溃。
苹果必须紧急检讨石英。
我在使用CGBitmapContextCreate(..)时遇到了同样的问题,泄漏随机发生并建立起来。
搜索:
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
或
provider = CGDataProviderCreateWithData((void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBufferFunction);
CGImageRef imageRef = CGImageCreate(cvMat.cols, // Width
cvMat.rows, // Height
8, // Bits per component
8 * cvMat.elemSize(), // Bits per pixel
cvMat.step, // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags
provider, // CGDataProviderRef
NULL, // Decode
false, // Should interpolate
kCGRenderingIntentDefault); // Intent
如果您使用的垃圾收集,使用CFMakeCollectable
我通过使用不同的功能从位图数据创建UIImage的解决了这个(posterFrame)。如果您使用传统的内存管理,这是非常简单的:
return (CGImageRef)[(id)posterFrame autorelease];
你投的CFTypeRef(在这种情况下,一个CGImageRef)到一个Objective-C对象的指针,向它发送-autorelease消息,然后投结果返回到CGImageRef。此模式适用于(几乎)与CFRetain()和CFRelease()兼容的任何类型。
我不知道,如果它涉及到泄漏,但是你不应该叫'无(位图数据)'你已经发布的上下文之前。用malloc()分配的内存块没有保留计数器,所以如果你调用'free()'它立即被释放,即使CGContext正在使用它。 – benzado 2009-12-30 20:37:41
你是针对iPhone OS 3.1还是更高?这可能是3.0或更早版本框架中的一个错误。 如果你使用3.1,你不需要为'CGBitmapContextCreate'分配bitmapData。 另外'_imageScaledToSize'是一个未记录的调用。请不要使用它。如果您使用无证电话,有些人可能不愿意回答您的问题。 – lucius 2010-01-12 03:57:57
嘿bbullis21你的模糊代码工作正常吗? – Leena 2012-01-05 14:24:05