Другой iPhone - CGBitmapContextCreateImage Leak - PullRequest
4 голосов
/ 16 сентября 2009

Как в этом посте:

У меня похожая проблема. Указатель из malloc в create_bitmap_data_provider никогда не освобождается. Я проверил, что связанный объект изображения в конце концов освобождается, а не выделение провайдера. Должен ли я явно создать поставщика данных и каким-то образом управлять его памятью? Похоже, взломать.

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, blah blah blah);
CGColorSpaceRelease(colorSpace);

// ... draw into context

CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage * image = [[UIImage alloc] initWithCGImage:imageRef];

CGImageRelease(imageRef);
CGContextRelease(context);

После ответа fbrereto ниже я изменил код так:

- (UIImage *)modifiedImage {
    CGSize size = CGSizeMake(width, height);

    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // draw into context   

    UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;  // image retainCount = 1
}    

// caller: 
{
    UIImage * image = [self modifiedImage]; 
    _imageView.image = image; // image retainCount = 2
}

// after caller done, image retainCount = 1, autoreleased object lost its scope

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

Я подтвердил, что мой объект dealloc вызван. RetainCount на _imageView.image и _imageView равны 1, прежде чем я выпущу _imageView. Это действительно не имеет смысла. Другие, похоже, тоже имеют эту проблему, я последний подозреваю SDK, но может ли здесь быть ошибка iPhone SDK ???

Ответы [ 5 ]

4 голосов
/ 15 июня 2012

Похоже, проблема в том, что вы освобождаете указатель на возвращенный CGImage, а не на CGImage. У меня тоже были похожие проблемы с постоянно растущими распределениями и возможным падением приложения. Я обратился к этому, выделив CGImage, а не CGImageRef. После внесения изменений запустите ваш код в Insturments с выделением ресурсов, и вы больше не увидите постоянное потребление памяти из malloc. Также, если вы используете метод класса imageWithCGImage, вам не придется беспокоиться об автоматическом освобождении вашего UIImage в дальнейшем.

Я набрал это на ПК, так что, если вы уроните его прямо в XCode, у вас могут возникнуть проблемы с синтаксисом, заранее извиняюсь; Однако основной звук.

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
CGContextRef context = CGBitmapContextCreate(NULL, blah blah blah); 
CGColorSpaceRelease(colorSpace);

// ... draw into context  

CGImage cgImage = CGBitmapContextCreateImage(context); 
UIImage * image = [UIImage imageWithCGImage:cgImage];  
CGImageRelease(cgImage); 
CGContextRelease(context);
return image;
3 голосов
/ 15 мая 2014

У меня была эта проблема, и это сводило меня с ума на несколько дней. После долгих копаний и обнаружения утечек в CG Raster Data с использованием инструментов.

Проблема, похоже, заключается в CoreGraphics. Моя проблема заключалась в том, что когда я использовал CGBitmapContextCreateImage в тесном цикле, он в течение некоторого времени сохранял некоторые изображения (по 800 КБ каждое), и это медленно вытекало.

После нескольких дней трассировки с помощью инструментов я обнаружил, что обходной путь - использовать вместо этого метод CGDataProviderCreateWithData. Интересно было то, что на выходе был тот же самый CGImageRef, но на этот раз не будет растровых данных CG, используемых в VM графикой Core, и не будет утечки. Я предполагаю, что это внутренняя проблема или неправильно ее использовал.

Вот код, который спас меня:

@autoreleasepool {
    CGImageRef cgImage;
    CreateCGImageFromCVPixelBuffer(pixelBuffer,&cgImage);

    UIImage *image= [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp];

    // DO SOMETHING HERE WITH IMAGE

    CGImageRelease(cgImage);
}

Ключ использовал CGDataProviderCreateWithData в методе ниже.

static OSStatus CreateCGImageFromCVPixelBuffer(CVPixelBufferRef pixelBuffer, CGImageRef *imageOut)
    {
        OSStatus err = noErr;
        OSType sourcePixelFormat;
        size_t width, height, sourceRowBytes;
        void *sourceBaseAddr = NULL;
        CGBitmapInfo bitmapInfo;
        CGColorSpaceRef colorspace = NULL;
        CGDataProviderRef provider = NULL;
        CGImageRef image = NULL;

        sourcePixelFormat = CVPixelBufferGetPixelFormatType( pixelBuffer );
        if ( kCVPixelFormatType_32ARGB == sourcePixelFormat )
            bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipFirst;
        else if ( kCVPixelFormatType_32BGRA == sourcePixelFormat )
            bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst;
        else
            return -95014; // only uncompressed pixel formats

        sourceRowBytes = CVPixelBufferGetBytesPerRow( pixelBuffer );
        width = CVPixelBufferGetWidth( pixelBuffer );
        height = CVPixelBufferGetHeight( pixelBuffer );

        CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
        sourceBaseAddr = CVPixelBufferGetBaseAddress( pixelBuffer );

        colorspace = CGColorSpaceCreateDeviceRGB();

        CVPixelBufferRetain( pixelBuffer );
        provider = CGDataProviderCreateWithData( (void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBuffer);
        image = CGImageCreate(width, height, 8, 32, sourceRowBytes, colorspace, bitmapInfo, provider, NULL, true, kCGRenderingIntentDefault);

        if ( err && image ) {
            CGImageRelease( image );
            image = NULL;
        }
        if ( provider ) CGDataProviderRelease( provider );
        if ( colorspace ) CGColorSpaceRelease( colorspace );
        *imageOut = image;
        return err;
    }

    static void ReleaseCVPixelBuffer(void *pixel, const void *data, size_t size)
    {
        CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)pixel;
        CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
        CVPixelBufferRelease( pixelBuffer );
    }
0 голосов
/ 27 октября 2013

Это действительно помогло мне! Вот как я использовал это, чтобы исправить эту неприятную проблему утечки:

    CGImage *cgImage = CGBitmapContextCreateImage(context);
    CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
    CGImageRelease(cgImage);
    image->imageRef = dataRef;
    image->image = CFDataGetBytePtr(dataRef);

Обратите внимание, мне пришлось хранить CFDataRef (для CFRelease (image-> imageRef)) в моей функции ~ Image. Надеюсь, это также помогает другим ... JR

0 голосов
/ 17 сентября 2009

Вы не единственный с этой проблемой. У меня были серьезные проблемы с CGBitmapContextCreateImage (). Когда вы включаете режим Zombie , он даже предупреждает вас, что память освобождается дважды (если это не так). Определенно есть проблема при смешивании CG * с UI *. Я все еще пытаюсь понять, как кодировать эту проблему.

Примечание : вызов UIGraphicsBeginImageContext не является потокобезопасным. Будьте осторожны.

0 голосов
/ 16 сентября 2009

Вместо того, чтобы вручную создавать CGContextRef, я бы предложил вам использовать UIGraphicsBeginImageContext, как показано в в этом посте . Более подробную информацию об этом наборе подпрограмм можно найти здесь . Я верю, что это поможет решить эту проблему или, по крайней мере, даст вам меньше памяти, чтобы управлять собой.

UPDATE:

Учитывая новый код, retainCount UIImage в том виде, как оно выходит из функции, будет равно 1, а присвоение его изображению imageView приведет к его увеличению до 2. В этот момент освобождение imageView приведет к оставьте retainCount из UIImage равным 1, что приведет к утечке. Затем, после присвоения UIImage для imageView, важно release it. Это может выглядеть немного странно, но это заставит retainCount быть правильно установленным в 1.

...