5

Хорошо, у меня есть мир трудности, отслеживающий эту утечку памяти. При запуске этого сценария я не вижу утечки памяти, но мой объектalloc поднимается. Инструменты указывают на CGBitmapContextCreateImage> create_bitmap_data_provider> malloc, это занимает 60% моего объекта allococ.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'. Также '_imageScaledToSize' является недокументированным звонком. Пожалуйста, не используйте его. Некоторые люди могут не захотеть ответить на ваш вопрос, если вы используете недокументированные звонки. – lucius

+0

эй bbullis21 ваш код размытия работает нормально на устройстве ?? – Leena

ответ

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 не будет выпущен, но image1 и image2 будет.

+0

Вы не должны '-release' после' -drain' (что эквивалентно '-release' при сборке вне мусора), поскольку это вызывает двойной выпуск. – kennytm

1

Получите clang и запустите свой проект через него. Мало того, что он найдет ваши (статические) утечки, это поможет вам лучше понять правила подсчета ссылок - по крайней мере, для меня.

+1

Или запустите «Build & Analyze» в Xcode 3.2, если вы используете Snow Leopard. Это clang и делает хорошую работу по диаграммированию потока исполнения, приводящего к проблеме. –

0

Некоторые идеи/мысли:

С одной стороны, этот кусок кода, очевидно, имеет проблемы, так как вы можете свободно BitmapData дважды, если контекст не может выделить.

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

    CGColorSpaceRelease(colorSpace); 
    free (bitmapData); 

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

Я также согласен с benzado, что вы BitmapData, пока вы не закончите с контекстом ... хотя это не сбой, который загадочным. повезло, возможно.

Обратите внимание, что я уверен, что поставщик данных и биты это относится к собираюсь стать частью cgImage возвращаемого из CGImageCreate:

поставщика Источника данных для точечного рисунка. Информацию о поддерживаемых форматах данных см. В обсуждении ниже. Кварц сохраняет этот объект; по возвращении вы можете спокойно освободить его.

Итак, это означает, что до тех пор, пока вы не вернете UIImage, я не думаю, что поставщик данных cgImage или бит данных уйдет. Итак, возможно, проблема в том, что UIImage не каждый уходит?

Почему вы не используете CGBitmapContextCreateImage() для создания результирующего изображения? (вам может потребоваться вызвать CGContextFlush(), чтобы заставить рисовать в растровое изображение, но похоже, что вы все вручную меняете пиксель, поэтому, возможно, не нужно).

Основываясь на расплывчатой ​​документации, я не думаю, что вы должны быть свободными() указателем, возвращаемым CGBitmapContextGetData (но это немного догадка из-за неопределенности документов).

также:

вы не инициализируется srcData, destData & FinalData в NULL, чтобы ваш тест до освобождения() ИНГ им кажется рискованным.

все, что сказал (и не попробовать некоторые из этих вещей), мы имели утечку в нашем Mac App в одной точке, так как в 10.5.6 и выше некоторого аспекта CIImage imageWithCGImage, imageByApplyingTransofrm, imageByCroppingToRect, NSBitmapImageRep initWithCIImage или NSBitmapImageRep CGImage утечка. Это было исправлено в 10.5.7, поэтому вполне возможно, что что-то подобное существует в версии iPhone OS, с которой вы тестируете.

0

На самом деле, если я помню, [UIImage imageNamed] создает свой собственный кеш, который находится вне вашего контроля. Это будет учитывать использование памяти, которую вы видите. Вы можете узнать больше здесь - http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/.

4

Я использовал кварц много раз. Каждый раз, когда я делаю, это кошмар. Насколько я заметил, и я заполнил ошибку на Apple, отправив проект как доказательство аварии, один из 4-х вещей верно:

  1. Quartz утечки, как ад
  2. Это займет слишком много времени, чтобы освободить память
  3. он использует слишком много памяти без необходимости или
  4. комбинация из всех них.

Я однажды создал простой проект, чтобы доказать это. У проекта была кнопка. Каждый раз, когда нажималась кнопка, на экран добавлялось небольшое изображение (100x100 пикселей). Изображение состояло из двух слоев, самого изображения и дополнительного слоя, содержащего пунктирную линию, нарисованную вокруг границы изображения. Этот рисунок был сделан в Кварце. Нажатие 8-кратной кнопки запустило приложение. Вы можете сказать: нажатие кнопки 8 раз и 16 изображений было добавлено на экран, это причина сбоя, не так ли?

Вместо того, чтобы рисовать границу с помощью кварца, я решил провести предварительную рисование границы на PNG и просто добавить его как слой к объекту. Те же два слоя на каждый созданный объект. Правильно?

Я нажал 100 раз, добавив 200 изображений на экран и без сбоев. Память никогда не превышала 800 Kb. Я мог бы продолжать нажимать ... приложение продолжалось быстро и быстро. Нет предупреждения о памяти, нет сбоев.

Apple должна тщательно рассмотреть кварц.

0

У меня была такая же проблема при использовании CGBitmapContextCreate (..) с утечками, происходящими случайным образом и наращиванием.

Я решил эту проблему, создав UIImage из растровых данных, используя различные функции:

Try:

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

Вы бросили CFTypeRef (в данном случае, CGImageRef) в качестве указателя объекта Objective-C, послать ему сообщение -autorelease, а затем отливали результат возвращается к CGImageRef. Этот шаблон работает почти для любого типа, совместимого с CFRetain() и CFRelease().

How to Autorelease a CGImageRef?