2009-09-15 123 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?