Cause of big memory allocation (memory leak?) when

2019-04-16 07:10发布

问题:

I try to modify FGallery (https://github.com/gdavis/FGallery-iPhone). I need it to read images from the camera roll, but I get memory leak.

Old code (path is file location):

@autoreleasepool {

NSString *path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle]   bundlePath],_thumbUrl];
_thumbnail = [UIImage imageWithContentsOfFile:path];
_hasThumbLoaded = YES;
_isThumbLoading = NO;
[self performSelectorOnMainThread:@selector(didLoadThumbnail) withObject:nil   waitUntilDone:YES];
}

My code (path is assert library url):

@autoreleasepool {

ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
   ALAssetRepresentation *rep = [myasset defaultRepresentation];
   CGImageRef iref = [rep fullResolutionImage];
   if (iref) {
       _thumbnail = [UIImage imageWithCGImage:iref];
       _hasThumbLoaded = YES;
       _isThumbLoading = NO;
       [self performSelectorOnMainThread:@selector(didLoadThumbnail) withObject:nil   waitUntilDone:YES];
   }
};        

ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror) {
   NSLog(@"booya, cant get image - %@",[myerror localizedDescription]);
};     

NSURL *asseturl = [NSURL URLWithString:_thumbUrl];
[assetslibrary assetForURL:asseturl 
resultBlock:resultblock
failureBlock:failureblock];
}
}

For the same images, I get a big memory allocation (-didReceiveMemoryWarning) that crash the program in my code, but not while using the original code.

Any ideas why?

P.S. I use ARC, and did the automatic transition for FGallery. It works fine for local app images, but as said, I can't make it to work for camera roll images.

edit 1: the program crash

回答1:

i think i got it. the "ALAssetsLibraryAssetForURLResultBlock resultblock" is running on a different thread. and so the "@autoreleasepool" does not apply to it. (each thread have it's own autorelease pool). hence, the memory footprint is much higher due to lot of "autoreleased" allocations (images). adding "@autoreleasepool" inside the block stopped the crashs and big memory allocations.

in short:

ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
   @autoreleasepool {
      ALAssetRepresentation *rep = [myasset defaultRepresentation];
      CGImageRef iref = [rep fullResolutionImage];
      if (iref) {
          _thumbnail = [UIImage imageWithCGImage:iref];
          _hasThumbLoaded = YES;
          _isThumbLoading = NO;
          [self performSelectorOnMainThread:@selector(didLoadThumbnail) withObject:nil   waitUntilDone:YES];
       }
   }
}; 

thanks to all who replied.



回答2:

Unless there is some need for the full resolution image, you would likely be better off using:

CGImageRef iref = [rep fullScreenImage];

This call return a CGImage of the representation that is appropriate for displaying full screen rather than the biggest, best representation available, unadjusted in any way.

Doing so will save loads of memory.



回答3:

Getting a memory warning (-didReceiveMemoryWarning) is not the same as having a memory leak.It just means you have a lot of memory allocated and it's putting pressure on the system to where the OS interprets this as a potential problem that may occur soon.

A memory leak happens when you have unreferenced objects that did not get released. You can use the compiler analysis tool to see where potential leaks are. That won't find them all, so you can use instruments to see where any others may be happening. But until you have checked with those tools, you can't say for sure that you have a leak that isn't obvious by looking at the code.

You didn't mention if your code is crashing, but if it is, it is not necessarily due to a memory leak. That could happen when the OS decides something has to be removed to reduce memory pressure.

UPDATE

Show the code for the class ALAssetRepresentation . You may not be releasing something in there.



回答4:

As user Picciano mentioned in his post, if possible you should use the [rep fullScreenImage]; call instead of requesting the full-size image. This will save a lot of space. However, in my case this wasn't possible because I need to send a higher-res image to an external server later. What you can do is use the scale to resize it as much as possible:

CGFloat originalRatio = assetRepresentation.dimensions.width / assetRepresentation.dimensions.height;
CGFloat wantedRatio = maxSize.width / maxSize.height;
CGFloat scale = 1;

if (originalRatio < wantedRatio)
{
    scale = maxSize.height / assetRepresentation.dimensions.height;
}
else
{
    scale = maxSize.width / assetRepresentation.dimensions.width;
}

CGImageRef ref = [assetRepresentation fullResolutionImage];
UIImage *image = [UIImage imageWithCGImage:ref
                                     scale:scale orientation:orientation];

What this basically does it determine the amount we can scale the image (defined by maxSize). This saved us enough to prevent memory leaks.