2009-09-15 199 views
5

好吧我有一个难以追踪这个内存泄漏的世界。运行这个脚本时,我没有看到任何内存泄漏,但我的对象正在爬升。 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] ...但如何做我释放它已经返回后?希望有人可以帮忙。

+1

我不知道,如果它涉及到泄漏,但是你不应该叫'无(位图数据)'你已经发布的上下文之前。用malloc()分配的内存块没有保留计数器,所以如果你调用'free()'它立即被释放,即使CGContext正在使用它。 – benzado 2009-12-30 20:37:41

+0

你是针对iPhone OS 3.1还是更高?这可能是3.0或更早版本框架中的一个错误。 如果你使用3.1,你不需要为'CGBitmapContextCreate'分配bitmapData。 另外'_imageScaledToSize'是一个未记录的调用。请不要使用它。如果您使用无证电话,有些人可能不愿意回答您的问题。 – lucius 2010-01-12 03:57:57

+0

嘿bbullis21你的模糊代码工作正常吗? – Leena 2012-01-05 14:24:05

回答

0

我见过类似的行为,并阅读了其他几个类似的帖子。创建一个自动释放池似乎没有帮助。它看起来像库泄漏内存或将其存储在更高级别的自动发布池中,因此它不会被释放,直到太晚。我怀疑在框架中有一个错误,但不能证明它。

0

它帮助,如果你确实包裹图像的分配,并在池对象存在周期释放,

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会。

+0

在'-drain'(这相当于非垃圾回收运行时的'-release')之后''不应该'释放',因为这会导致双重版本。 – kennytm 2010-01-14 05:34:11

1

获取clang并通过它运行您的项目。它不仅会发现你的(静态)泄漏,还会帮助你更多地了解引用计数规则 - 至少它对我来说是这样。

+1

或者如果您正在运行Snow Leopard,请在Xcode 3.2中运行“Build&Analyze”。这是铿锵声,并做出了很好的图表导致问题的执行流程图。 – 2010-03-29 14:55:52

0

一些想法/想法:

原因之一,这个块的代码显然有问题,因为你可以自由的位图数据的两倍,如果上下文无法分配。

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版本中可能存在类似的情况。

4

我已经多次使用Quartz。每次我做的都是噩梦。至于我注意到,我已经填补了漏洞苹果发送一个项目作为碰撞的证据,4件事情之一是真的:

  1. 石英泄漏的地狱
  2. 花了很长时间来释放内存
  3. 它不必要地使用太多的内存或
  4. 它们的组合。

我曾经创建过一个简单的项目来证明这一点。该项目有一个按钮。每按一次按钮,一个小图像(100x100像素)就被添加到屏幕上。图像由两层组成,图像本身和一个附加层,其中包含围绕图像边框绘制的虚线。这张图是在Quartz中完成的。按下按钮8次使应用程序崩溃。你可以这样说:按下按钮8次,屏幕上添加了16张图片,这就是崩溃的原因,对吧?

我没有使用Quartz绘制边框,而是决定在PNG上预先绘制边框,并将其作为图层添加到对象中。每个对象创建相同的两个图层。对?

我点击了100次,在屏幕上添加了200张图片,没有崩溃。内存永远不会超过800 Kb。我可以继续点击...应用程序继续更快捷。没有记忆警告,没有崩溃。

苹果必须紧急检讨石英。

0

我在使用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 
0

如果您使用的垃圾收集,使用CFMakeCollectable

我通过使用不同的功能从位图数据创建UIImage的解决了这个(posterFrame)。如果您使用传统的内存管理,这是非常简单的:

return (CGImageRef)[(id)posterFrame autorelease]; 

你投的CFTypeRef(在这种情况下,一个CGImageRef)到一个Objective-C对象的指针,向它发送-autorelease消息,然后投结果返回到CGImageRef。此模式适用于(几乎)与CFRetain()和CFRelease()兼容的任何类型。

How to Autorelease a CGImageRef?