Задача C UIImagePNGRПредставление памяти проблема (с использованием ARC) - PullRequest
7 голосов
/ 20 марта 2012

У меня есть приложение на базе ARC, которое загружает из веб-службы около 2000 довольно больших (1–4 МБ) изображений в кодировке Base64. Он преобразует декодированные строки Base64 в файлы изображений .png и сохраняет их на диск. Все это делается в цикле, где у меня не должно быть никаких затяжных ссылок.

Я профилировал свое приложение и обнаружил, что UIImagePNGRepresentation занимает около 50% доступной памяти.

Как я вижу, UIImagePNGRepresentation кеширует изображения, которые создает. Один из способов исправить это - очистить кеш. Есть идеи, как можно это сделать?

Другим решением было бы использовать что-то, кроме UIImagePNGRepresentation?

Я уже попробовал это безуспешно: Ошибка памяти при использовании UIImagePNGRepresentation . Не говоря уже о том, что я не могу реально использовать предоставленное там решение, потому что оно сделает мое приложение слишком медленным.

Это метод, который я вызываю из моего цикла. UIImage - изображение, преобразованное из Base64:

+ (void)saveImage:(UIImage*)image:(NSString*)imageName:(NSString*)directory {
  NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.
  NSFileManager *fileManager = [NSFileManager defaultManager];
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it
  NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory
  NSString *pathToFolder = [documentsDirectory stringByAppendingPathComponent:directory];

  if (![fileManager fileExistsAtPath:pathToFolder]) {
    if(![fileManager createDirectoryAtPath:pathToFolder withIntermediateDirectories:YES attributes:nil error:NULL]) {
       // Error handling removed for brevity
    }
  }

  NSString *fullPath = [pathToFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path
  [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image)

  // clear memory (this did nothing to improve memory management)
  imageData = nil;
  fileManager = nil; 
}

EDIT: Размеры изображения варьируются примерно от 1000 * 800 до 3000 * 2000.

Ответы [ 7 ]

4 голосов
/ 20 марта 2012

Вы можете обернуть тело метода пулом авто-релиза

+ (void)saveImage:(UIImage*)image:(NSString*)imageName:(NSString*)directory {

    @autoreleasepool
    {
        NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it
        NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory
        NSString *pathToFolder = [documentsDirectory stringByAppendingPathComponent:directory];

        if (![fileManager fileExistsAtPath:pathToFolder]) {
          if(![fileManager createDirectoryAtPath:pathToFolder withIntermediateDirectories:YES attributes:nil error:NULL]) {
             // Error handling removed for brevity
          }
        }

        NSString *fullPath = [pathToFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path
        [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image)
    }
}

Но на самом деле это может быть полезно, если вы предоставите нам еще несколько чисел:
Какие размеры имеют изображения.Это важно, так как данные изображения хранятся в памяти в виде необработанных пикселей.т.е. изображение 2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB.Теперь представьте, что алгоритм преобразования должен будет хранить информацию для каждого пикселя или хотя бы некоторой области.Ожидаются огромные цифры.

1 голос
/ 02 декабря 2013

У меня была такая же проблема с UIImagePNGRepresentation и ARC. Мой проект генерирует тайлы, и выделенная память UIImagePNGRepresentation просто не удалялась, даже когда вызов UIImagePNGRepresentation является частью @ autoreleasepool.

Мне не повезло, что проблема исчезла, добавив еще несколько @ autoreleasepool, как это было для JHollanti.

Мое решение основано на идее EricS, использующей ImageIO Framework для сохранения файла png:

-(void)saveImage:(CGImageRef)image directory:(NSString*)directory filename:(NSString*)filename  {
@autoreleasepool {
    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", directory, filename]];
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
    CGImageDestinationAddImage(destination, image, nil);

    if (!CGImageDestinationFinalize(destination))
        NSLog(@"ERROR saving: %@", url);

    CFRelease(destination);
    CGImageRelease(image);
}

}

Самое главное - выпустить изображение потом: CGImageRelease (image);

0 голосов
/ 22 декабря 2016

Я решаю эту проблему, отправляя изображение с 4 каналами (RGBA или RGBX) вместо изображения с 3 каналами (RGB).Вы можете проверить, есть ли шанс изменить параметры вашего изображения.

Когда вы конвертируете Base64 в UIImage, попробуйте использовать kCGImageAlphaNoneSkipLast вместо kCGImageAlphaNone.

0 голосов
/ 10 февраля 2014

Просто один из возможных способов - использовать библиотеки github, которые загружают и кэшируют UIImage / NSData из Интернета. Это может быть SDWebImage (https://github.com/rs/SDWebImage) или APSmartStorage (https://github.com/Alterplay/APSmartStorage). Latest) получает изображение из Интернета и сохраняет его на диске и в памяти.

0 голосов
/ 22 августа 2012

Попробуйте это:

+ (void)saveImage:(UIImage*)image:(NSString*)imageName:(NSString*)directory {
  NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.
  CFDataRef imageDataRef = (__bridge_retained CFDataRef) imageData; // ARC fix
  NSFileManager *fileManager = [NSFileManager defaultManager];
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it
  NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory
  NSString *pathToFolder = [documentsDirectory stringByAppendingPathComponent:directory];

  if (![fileManager fileExistsAtPath:pathToFolder]) {
    if(![fileManager createDirectoryAtPath:pathToFolder withIntermediateDirectories:YES attributes:nil error:NULL]) {
       // Error handling removed for brevity
    }
  }

  NSString *fullPath = [pathToFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path
  [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image)

  // clear memory (this did nothing to improve memory management)
  imageData = nil;
  CFRelease(imageDataRef); // ARC fix
  fileManager = nil;
}
0 голосов
/ 20 марта 2012

Возможно, вам повезет больше с The ImageIO Framework (PDF).Он имеет немного больше контроля над памятью и кэшированием, чем UIKit.

0 голосов
/ 20 марта 2012

Нужно ли конвертировать данные здесь, возможно, конвертировать их при загрузке с диска?

Объект NSData может сделать его NSMutableData таким образом, память для него выделяется один раз и увеличивается по мере необходимости.

...