2009-09-15 15 views
5

申し訳ありません。このメモリリークを追跡するのが難しい世界です。このスクリプトを実行すると、メモリが漏れることはありませんが、私のobjectallocは上昇しています。 InstrumentsはCGBitmapContextCreateImage> create_bitmap_data_provider> mallocを指していますが、これは私のobjectallocの60%を占めています。iPhone - UIImage Leak、CGBitmapContextCreateImage Leak

このコードは、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

それがリークに関連しています場合、私は考えているが、あなたはコンテキストをリリースしてきた前に、 '(のBitmapData)自由'を呼び出すべきではありません。 'malloc()'で割り当てられたメモリのブロックは保持カウンタを持たないので、 'free()'を呼び出すとCGContextがそれを使っていてもすぐに解放されます。 – benzado

+0

iPhone OS 3.1以降をターゲットに設定していますか?これは、3.0以前のフレームワークのバグかもしれません。 3.1の場合、 'CGBitmapContextCreate'にbitmapDataを割り当てる必要はありません。 また、 '_imageScaledToSize'は文書化されていない呼び出しです。それを使用しないでください。あなたが文書化されていない呼び出しを使用している場合、あなたの質問に答えてくれる人がいないかもしれません。 – lucius

+0

ちょっとbbullis21あなたのぼかしコードは、デバイス上で正常に動作していますか? – Leena

答えて

0

私は同様の動作を見て、いくつかの他の同様の記事を読んだ。自動解放プールを作成することは役に立たないようです。ライブラリがメモリを漏らしているか、またはそれを高レベルの自動リリースプールに格納しているので、あまりにも遅くまで解放されていないようです。フレームワークにバグがありますが、それを証明することはできません。

0

あなたが実際に画像の割り当てをラップし、プールオブジェクトの存在サイクルでリリースするかどうかはimage3.pngが解放されることはありませんが、画像1と画像2がなります。この例では

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]; 

、役立ちます。

+0

'-drain'(ガベージコレクションされていないランタイムでは' -release'と同じです)の後に '-release'してはいけません。 – kennytm

1

clangを取得し、プロジェクトを実行します。あなたの(静的な)漏れを見つけるだけでなく、それはあなたが参照カウントのルールについてもっと理解するのを助けます - 少なくとも私のためにしました。

+1

また、Snow Leopardを実行している場合は、Xcode 3.2で "Build&Analyze"を実行してください。それは不運であり、問​​題に至る実行の流れを図解する素晴らしい仕事をしています。 –

0

いくつかのアイデア/思考:

1のコンテキストを割り当てに失敗した場合は、二回のBitmapDataを解放することができるので、このコードの塊は明らかに問題があります。

if (context == NULL) { free (bitmapData); } 

    CGColorSpaceRelease(colorSpace); 
    free (bitmapData); 

    if (context == NULL) { return NULL; } 

は、私はまた、あなたが文脈...で行われるまで、それがクラッシュしていないが、あなたのBitmapData不可解さbenzadoに同意します。おそらく幸運。

私は、データプロバイダおよびビットかなり確信して注意がcgImageの一部になろうとしているを意味しているがCGImageCreateから返さ:

プロバイダ ビットマップのデータのソースを。サポートされるデータ形式については、以下の説明を参照してください。クォーツはこのオブジェクトを保持します。その返却時に安全に解放することができます。

これは、返されるUIImageがリリースされるまで、私はcgImageやビットデータプロバイダがなくなるとは思わないことを意味します。だから、UIImageがすべて消えていくわけではないのだろうか?

CGBitmapContextCreateImage()を使用してイメージを作成しているのはなぜですか? (ビットマップへの描画を強制するにはCGContextFlush()を呼び出す必要があるかもしれませんが、すべてのピクセルを手動で変更しているように見えるので、必要ではない可能性があります)。

漠然としたドキュメントに基づいて、私はあなたがCGBitmapContextGetDataによって返されたポインタを自由にするべきではないと思っています(しかし、それはドキュメントの漠然としたものではありません)。また

あなたがそれらをINGの ')(解放する前ので、あなたのテストをNULLにsrcData、destdataは変換& finalDataを初期化していないが危険なようです。

言った(とそれらのもののいくつかを試してみてくださいません)そのすべてが、我々は理由10.5.6とCIImage imageWithCGImage、imageByApplyingTransofrm、imageByCroppingToRect、NSBitmapImageRep initWithCIImageまたはNSBitmapImageRep CGImageの以前のいくつかの局面では一点で私たちのMacのAppでリークが発生していました漏れたこれは10.5.7で修正されていますので、あなたがテストしているiPhone OSバージョンに類似したものが存在する可能性があります。

0

実際に[UIImage imageNamed]があなたのコントロールの外にある独自のキャッシュを作成することを思い出してください。それはあなたが見ているメモリ使用量を考慮に入れます。詳細はこちら - http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/です。

4

私はQuartzを何度も使用しています。私がするたびに悪夢です。限り私は気づいた、と私は4つの物事のいずれかに該当する、クラッシュの証拠としてプロジェクトを送付アップルにバグを満たしているとして:地獄のよう

  1. クォーツ漏れをそれがメモリを解放するにはあまりにも時間がかかり
  2. 不必要に多くのメモリを使用するか、またはすべてを組み合わせて
  3. を使用します。

私はかつてこれを証明する簡単なプロジェクトを作成しました。プロジェクトにはボタンがありました。ボタンを押すたびに、小さな画像(100×100ピクセル)が画面に追加されました。イメージは2つのレイヤーで構成され、イメージ自体と、イメージ境界線の周囲に描かれた破線を含む追加のレイヤーで構成されていました。この作図はQuartzで行われました。ボタンを8回押すと、アプリケーションがクラッシュしました。あなたは言うことができます:ボタンを8回押すと16枚の画像が画面に追加されましたが、これがクラッシュする理由です。

Quartzを使用して枠線を描画するのではなく、境界線をPNGに描画して、オブジェクトとしてレイヤーとして追加することにしました。オブジェクトごとに同じ2つのレイヤーが作成されます。右?

100回クリックして200枚の画像を追加し、クラッシュしません。記憶は800Kbを超えることはありませんでした。私はクリックを続けることができました...アプリはすこし速く続きました。メモリ警告なし、クラッシュなし。

AppleはQuartzを緊急にレビューしなければなりません。

0

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 
0

を使用すると、ガベージコレクションを使用している場合は、CFMakeCollectableを使用(posterFrame)。あなたは伝統的なメモリ管理を使用している場合、それは非常に簡単です:

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

あなたはそれを-autoreleaseメッセージを送信し、Objective-Cのオブジェクトポインタに(この場合は、CGImageRef)CFTypeRefをキャストした後、キャストその結果をCGImageRefに返します。このパターンは、CFRetain()およびCFRelease()と互換性のある(ほとんど)すべての型に対して機能します。

How to Autorelease a CGImageRef?