iPhone - UIImage Leak, CGBitmapContextCreateImage Leak - PullRequest
5 голосов
/ 15 сентября 2009

Хорошо, я испытываю трудности с поиском утечки памяти. При запуске этого скрипта я не вижу утечек памяти, но у меня поднимается объект. Инструменты указывают на CGBitmapContextCreateImage> create_bitmap_data_provider> malloc, это занимает 60% моего objectalloc.

Этот код вызывается несколько раз с помощью 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]; ... но как мне освободить его после его возвращения? Надеюсь, кто-то может помочь, пожалуйста.

Ответы [ 8 ]

4 голосов
/ 18 марта 2010

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

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

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

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

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

Apple должна срочно пересмотреть Quartz.

1 голос
/ 08 января 2010

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

0 голосов
/ 30 августа 2014

Если вы используете сборщик мусора, используйте CFMakeCollectable (posterFrame). Если вы используете традиционное управление памятью, это очень просто:

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

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

Как автоматически выпустить CGImageRef?

0 голосов
/ 15 мая 2014

У меня была такая же проблема при использовании 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 голосов
/ 24 февраля 2010

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

0 голосов
/ 30 января 2010

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

С одной стороны, этот кусок кода явно имеет проблемы, поскольку вы можете дважды освобождать 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, поэтому ваш тест до их free () кажется рискованным.

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

0 голосов
/ 26 октября 2009

полезно, если вы на самом деле переносите распределение и освобождение образа в цикле существования объекта пула,

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 голосов
/ 25 сентября 2009

Я видел похожее поведение и прочитал несколько других похожих постов. Создание пула автоматического выпуска, похоже, не помогает. Похоже, что библиотека либо утечка памяти, либо хранит ее в пуле авто-релизов более высокого уровня, поэтому она не будет освобождена до слишком поздно. Я подозреваю, что во фреймворке есть ошибка, но не могу ее доказать.

...