UIGraphicsGetImageFromCurrentImageContext утечка памяти с предварительным просмотром - PullRequest
16 голосов
/ 25 февраля 2011

Я пытаюсь создать превью изображений страниц в PDF но у меня есть некоторые проблемы с освобождением памяти.

Я написал простой тестовый алгоритм, который циклично решает проблему, приложение падает около 40-й итерации :

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:@"myPdf.pdf"];
CFURLRef url = CFURLCreateWithFileSystemPath( NULL, (CFStringRef)pdfPath, kCFURLPOSIXPathStyle, NO );
CGPDFDocumentRef myPdf = CGPDFDocumentCreateWithURL( url );
CFRelease (url);
CGPDFPageRef page = CGPDFDocumentGetPage( myPdf, 1 );

int i=0;
while(i < 1000){

    UIGraphicsBeginImageContext(CGSizeMake(768,1024));
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,CGRectMake(0, 0, 768, 1024));
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, 1024);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextDrawPDFPage(context, page);
    CGContextRestoreGState(context);

    // --------------------------
    // The problem is here (without this line the application doesn't crash)
    UIImageView *backgroundImageView1 = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
    // --------------------------

    UIGraphicsEndImageContext();
    [backgroundImageView1 release];

    NSLog(@"Loop: %d", i++);
}

CGPDFDocumentRelease(myPdf);

Кажется, что вышеупомянутая строка вызывает утечку памяти, однако инструменты не показывают проблем с памятью;

Могу ли я уйти от такой ошибки? Кто-то может объяснить мне, каким образом? Есть ли другие способы показать предварительный просмотр PDF?

UPDATE

Я думаю, что проблема не в выпуске UIImage, созданном по методу UIGraphicsGetImageFromCurrentImageContext(), а в выпуске UIImageView, созданном с этим образом автоматического выпуска.

Я разделил строку кода на три шага:

UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIImageView *myImageView = [[UIImageView alloc] init];
[myImageView setImage: myImage]; // Memory Leak

Первая и вторая строки не создают утечек памяти, поэтому я думаю, что метод UIGraphicsGetImageFromCurrentImageContext не является проблемой.

Я также попытался сделать следующее, но проблема сохраняется:

UIImageView *myImageView = [[UIImageView alloc] initWithImage:myImage];

Я думаю, что в выпуске UIImageView, содержащего UIImage со свойством autorelease, произошла утечка памяти.

Я пытался написать свой объект UIImageView, наследующий UIView, как объяснено в этом потоке .

Это решение работает, но не очень элегантно, это обходной путь, я бы предпочел использовать объект UIImageView для решения проблемы с памятью.

Ответы [ 6 ]

26 голосов
/ 25 февраля 2011

Проблема заключается в следующем:

UIGraphicsGetImageFromCurrentImageContext()

возвращает автоматически выпущенный UIImage. Пул автоматического выпуска удерживается на этом образе до тех пор, пока ваш код не вернет управление в runloop, чего вы долгое время не делали. Чтобы решить эту проблему, вы должны будете создавать и использовать новый пул автоматического выпуска на каждой итерации (или каждые несколько итераций) цикла while.

13 голосов
/ 26 мая 2014

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

UIImage *image = UIGraphicsGetImageFromCurrentImageContext()

в цикле удерживает память, несмотря на то, что я вызываю image = nil;Не уверен, как долго приложение будет удерживать память до освобождения, но моего приложения, безусловно, достаточно долго, чтобы получить предупреждение о памяти и затем произойти сбой.использует изображение из UIGraphicsGetImageFromCurrentImageContext () в @autoreleasepool.Итак, у меня есть:

@autoreleasepool {
    UIImage *image = [self imageWithView:_outputImageView]; //create the image
    [movie addImage:image frameNum:i fps:kFramesPerSec];    //use the image as a frame in movie
    image = nil;
}

Надеюсь, что это может кому-то помочь.

3 голосов
/ 06 октября 2018

Для дальнейшего использования вот что я сделал, чтобы решить эту проблему (протестировано в Swift 4).

Я вызывал функцию ниже для каждого нового изображения, загруженного из Интернета (в очереди утилит). Перед выполнением пула автоматического выпуска после обработки около 100 произойдет сбой.

Для простоты в функции resizeImage я удалил необходимый код, за исключением автозапуска и утечки.

private func resizeImage(image: UIImage, toHeight: CGFloat) -> UIImage {
    return autoreleasepool { () -> UIImage in
        [...]

        let newImage = UIGraphicsGetImageFromCurrentImageContext() //Leaked
        UIGraphicsEndImageContext()

        return newImage!
    }

}

Надеюсь, это поможет!

1 голос
/ 25 февраля 2011

Этот код работает в основном потоке?В документации UIGraphicsGetImageFromCurrentImageContext ( link ) говорится, что он должен работать именно так.

0 голосов
/ 30 мая 2018

У меня была такая же проблема при работе с большими изображениями. Добавление отступа к изображению или применение фильтра приводит к выделению 200 МБ памяти без освобождения, пока изображение находилось на экране.

Найден рабочий способ решения этой проблемы:

extension UIImage {
  func release() -> UIImage? {
    guard let data = UIImageJPEGRepresentation(self, 1.0) else { return nil }
    return UIImage(data: data)
  }
}

Также пробовал это с UIImagePNGRepresentation, но, похоже, не освобождает распределение ни.

0 голосов
/ 05 мая 2012

ваша строка сбоя, вы можете обновить ее следующим образом

вывести один UIimage из цикла

rendered_image = UIGraphicsGetImageFromCurrentImageContext ();

...