6

我有一个基于ARC的应用程序,它从Web服务中加载大约2,000个相当大(1-4MB)的Base64编码图像。它将Base64解码的字符串转换为.png图像文件并将其保存到磁盘。这一切都是在一个循环中完成的,我不应该有任何延续的引用。Objective C UIImagePNGRepresentation内存问题(使用ARC)

我对我的应用程序进行了剖析,发现UIImagePNGRepresentation占用了大约50%的可用内存。

我看到它的方式,UIImagePNGRepresentation缓存它创建的图像。解决这个问题的一种方法是刷新缓存。任何想法如何可以做到这一点?

另一种解决方案是使用UIImagePNGRepresentation以外的东西吗?

我已经试过这个没有运气:Memory issue in using 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; 
} 

编辑: 图片尺寸大致改变从1000 * 800到3000 * 2000。

回答

4

您可以通过一个自动释放池

+ (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) 
    } 
} 

包裹方法体但实际上它可能是有帮助的,如果你向我们提供一些更多的数字:
什么尺寸做的图像有。这很重要,因为图像数据以原始像素存储在内存中。 iE图片2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB。现在想象一下,转换算法必须存储每个像素或至少某个区域的信息。巨大的数字是可以预料的。

+0

我试过autoreleasepool,但它没有帮助。出于某种原因,当我尝试使用autoreleasepool进行配置文件时,该应用崩溃了。我会将图片尺寸添加到我的问题中。 – JHollanti 2012-03-20 08:15:49

+0

我将autoreleasepool thingie添加到一堆其他地方,整个事情开始奏效。如果我能够在包含autoreleasepools的时候对应用程序进行配置,那开始的时候会容易得多。 @vikingosegundo曾经有这个我正在面对的分析问题? – JHollanti 2012-03-20 20:26:56

+0

没有。但我经常用lldb调试和分析问题。我切换回gdb。 – vikingosegundo 2012-03-20 20:30:54

0

是否有必要在这里转换数据,也许在磁盘上的负载转换它?

NSData对象可能会通过这种方式使NSMutableData的内存分配一次,并根据需要增长。

+0

这是一个好主意,但我宁愿将图像作为图像,所以我不必担心在需要使用它的地方转换图像。 – JHollanti 2012-03-20 08:18:50

0

The ImageIO Framework(PDF)可能会有更好的运气。它比UIKit对内存和缓存有更多的控制。

+0

任何指针如何将base64字符串转换为使用ImageIO框架的图像? – JHollanti 2012-03-20 09:41:15

+0

http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html?m=1 – EricS 2012-03-20 14:26:25

0

试试这个:

+ (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; 
} 
1

我有同样的问题与UIImagePNGRepresentation和ARC。 即使UIImagePNGRepresentation调用是@autoreleasepool的一部分,我的项目正在生成瓷砖,并且通过UIImagePNGRepresentation分配的内存才被删除。

我没有幸运的问题是通过添加更多的@ autoreleasepool像JHollanti一样消失了。

我的解决方案是基于埃里克斯理念,使用的ImageIO框架保存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(图像);

+0

这完全解决了我们的问题。 – 2016-01-21 23:22:30

0

我通过发送4通道图像(RGBA或RGBX)而不是3通道图像(RGB)来解决此问题。 您可以检查是否有机会更改图像的参数。

当您将Base64转换为UIImage时,请尝试使用kCGImageAlphaNoneSkipLast而不是kCGImageAlphaNone