Task C UIImagePNGR Memory Problem (Using ARC)

I have an ARC-based application that downloads about 2,000 fairly large (1-4 MB) Base64 encoded images from a web service. It converts Base64 decoded strings to .png image files and saves them to disk. All this is done in a loop where I should not have any lingering links.

I profiled my application and found out that the UIImagePNGR view clogs about 50% of the available memory.

As I see it, UIImagePNGR represents the caching of the images they create. One way to fix this is to clear this cache. Any ideas how to do this?

Another solution would be to use something other than a UIImagePNGRview?

I already tried this with no luck: The memory issue when using UIImagePNGR is . Not to mention the fact that I really can’t use the provided solution, because it will make my application too slow.

This is the method that I call from my loop. UIImage is an image converted from 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: Image sizes range from approximately 1000 * 800 to 3000 * 2000.

+6
source share
7 answers

You can wrap the body of a method with an autodetection pool

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

But in fact, this can be useful if you provide us with a few more numbers:
What sizes are the images. This is important because image data is stored in raw pixels. 2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB image 2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB . Now imagine that the transformation algorithm should store information for each pixel or at least some area. Huge quantities are expected.

+4
source

I had the same problem with representing UIImagePNGR and ARC. My project generates tiles, and the allocated UIImagePNGR memory is simply not deleted, even when the UIImagePNGR call is part of @autoreleasepool.

I was not lucky that the problem disappeared by adding a few more @autoreleasepool, as it was for JHollanti.

My solution is based on EricS idea using ImageIO Framework to save png file:

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

}

The most important thing is to release the image afterwards: CGImageRelease (image);

+1
source

Is it necessary to convert data here, perhaps convert it when booting from disk?

An NSData object can make it an NSMutableData in such a way that memory is allocated once and grows as needed.

0
source

You may be lucky with the ImageIO Framework (PDF). It has a bit more control over memory and caching than UIKit.

0
source

Try the following:

 + (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
source

Only one possible way is to use github libs, which downloads and caches UIImage / NSData from the Internet. This can be SDWebImage ( https://github.com/rs/SDWebImage ) or APSmartStorage ( https://github.com/Alterplay/APSmartStorage ). The latter receives the image from the Internet and reliably stores it on disk and in memory.

0
source

I solve this problem by sending a 4-channel image (RGBA or RGBX) instead of a 3-channel image (RGB). You can check if there is a chance to change the parameters of your image.

When you convert Base64 to UIImage, try using kCGImageAlphaNoneSkipLast instead of kCGImageAlphaNone .

0
source

Source: https://habr.com/ru/post/911151/


All Articles