【问题标题】:iPhone - UIImage Leak, CGBitmapContextCreateImage LeakiPhone - UIImage 泄漏,CGBitmapContextCreateImage 泄漏
【发布时间】:2009-09-15 14:18:23
【问题描述】:

好吧,我很难追踪这个内存泄漏。运行此脚本时,我没有看到任何内存泄漏,但我的 objectalloc 正在攀升。 Instruments 指向 CGBitmapContextCreateImage > create_bitmap_data_provider > malloc,这占了我objectalloc的60%。

这段代码被 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];...但是在它被返回后我该怎么做呢?希望有人可以帮忙。

【问题讨论】:

  • 我不知道它是否与泄漏有关,但在释放上下文之前你不应该调用free(bitmapData)。使用malloc() 分配的内存块没有保留计数器,因此如果您调用free(),它会立即被释放,即使 CGContext 正在使用它。
  • 您的目标是 iPhone OS 3.1 或更高版本吗?这可能是 3.0 或更早版本框架中的错误。如果您使用的是 3.1,则无需为 CGBitmapContextCreate 分配 bitmapData。 _imageScaledToSize 也是一个无证电话。请不要使用它。如果您使用无证电话,有些人可能不愿意回答您的问题。
  • 嘿 bbullis21 你的模糊代码在设备上运行正常吗??

标签: iphone memory-leaks uiimage cgimage cgbitmapcontextcreate


【解决方案1】:

我已经多次使用 Quartz。每次我做的都是一场噩梦。据我所知,我已经填写了一个关于 Apple 发送项目作为崩溃证明的错误,4 件事之一是正确的:

  1. Quartz 泄漏得厉害
  2. 释放内存的时间过长
  3. 它不必要地使用了过多的内存或
  4. 所有这些的组合。

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

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

我点击了 100 次,将 200 张图片添加到屏幕上,并且没有崩溃。内存从未超过 800 Kb。我可以继续点击……该应用程序继续更快更快速。没有内存警告,没有崩溃。

Apple 必须紧急审查 Quartz。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 如果您正在运行 Snow Leopard,则在 Xcode 3.2 中运行“构建和分析”。它是铿锵的,并且很好地绘制了导致问题的执行流程。
    【解决方案3】:

    我看到过类似的行为并阅读了其他几篇类似的帖子。创建一个自动释放池似乎没有帮助。看起来该库要么泄漏了内存,要么将其存储在更高级别的自动释放池中,因此它直到为时已晚才被释放。我怀疑框架中存在错误,但无法证明。

    【讨论】:

      【解决方案4】:

      如果您将图像分配和释放实际包装在池对象存在周期中,它会有所帮助,

      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 不会被释放,但是 image1 和 image2 会被释放。

      【讨论】:

      • 你不应该在-drain 之后-release(这相当于非垃圾收集运行时的-release),因为这会导致双重释放。
      【解决方案5】:

      一些想法/想法:

      首先,这段代码显然存在问题,因为如果上下文分配失败,您可以释放 bitmapData 两次。

          if (context == NULL) { free (bitmapData); }
      
          CGColorSpaceRelease( colorSpace );
          free (bitmapData);
      
          if (context == NULL) { return NULL; }
      

      我也同意 benzado 的观点,即你使用 bitmapData 直到你完成上下文......虽然它没有崩溃,这令人费解。也许是幸运的。

      请注意,我很确定数据提供者和所引用的位将成为从 CGImageCreate 返回的 cgImage 的一部分:

      提供者 位图的数据源。有关支持的数据格式的信息,请参阅下面的讨论。 Quartz 保留了这个对象;返回时,您可以安全地释放它。

      所以,这意味着在您返回的 UIImage 被释放之前,我认为 cgImage 或 bits 数据提供者不会消失。那么,也许问题在于 UIImage 并非全部消失?

      为什么不直接使用 CGBitmapContextCreateImage() 来创建结果图像? (您可能需要调用 CGContextFlush() 来强制绘制位图,但看起来您正在手动更改所有像素,因此可能不需要)。

      根据模糊的文档,我认为您不应该对 CGBitmapContextGetData 返回的指针进行 free() 处理(但由于文档的模糊性,这有点猜测)。

      还有:

      您没有将 srcData、destData 和 finalData 初始化为 NULL,因此您在 free() 之前进行的测试似乎有风险。

      所有这些(并尝试其中的一些事情),我们的 Mac 应用程序中有一次泄漏,因为在 10.5.6 和更早的版本中,CIImage imageWithCGImage、imageByApplyingTransofrm、imageByCroppingToRect、NSBitmapImageRep initWithCIImage 或 NSBitmapImageRep CGImage 的某些方面泄漏了.此问题已在 10.5.7 中修复,因此您正在测试的 iPhone OS 版本中可能存在类似问题。

      【讨论】:

        【解决方案6】:

        实际上,如果我记得 [UIImage imageNamed] 会创建自己的缓存,这是您无法控制的。这将解释您所看到的内存使用情况。您可以在此处阅读更多信息 - http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/

        【讨论】:

          【解决方案7】:

          我在使用 CGBitmapContextCreate(..) 时遇到了同样的问题,泄漏随机发生并不断累积。

          我通过使用不同的函数从位图数据创建 UIImage 解决了这个问题:

          试试:

          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
          

          【讨论】:

            【解决方案8】:

            如果您使用垃圾收集,请使用 CFMakeCollectable(posterFrame)。如果您使用的是传统的内存管理,则非常简单:

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

            您将 CFTypeRef(在本例中为 CGImageRef)转换为 Objective-C 对象指针,向其发送 -autorelease 消息,然后将结果转换回 CGImageRef。此模式适用于(几乎)任何与 CFRetain() 和 CFRelease() 兼容的类型。

            How to Autorelease a CGImageRef?

            【讨论】:

              猜你喜欢
              • 2010-11-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-13
              • 1970-01-01
              相关资源
              最近更新 更多