Kaitis
Kaitis

Reputation: 638

memory leak when requesting photos using the Photos framework

I am using the following method to request a number of photos and add them to an array for later use:

-(void) fetchImages{

        self.assets = [[PHFetchResult alloc]init];
        PHFetchOptions *options = [[PHFetchOptions alloc] init];
        options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
        self.assets = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];

        self.photosToVideofy = [[NSMutableArray alloc]init];

        CGSize size = CGSizeMake(640, 480);
        PHImageRequestOptions *photoRequestOptions = [[PHImageRequestOptions alloc]init];
        photoRequestOptions.synchronous = YES;

        for (NSInteger i = self.firstPhotoIndex; i < self.lastPhotoIndex; i++)
        {
            PHAsset *asset = self.assets[i];
            [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFit options:photoRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
                if (result) {
                    [self.photosToVideofy addObject:result];


                }
            }];
        }
        NSLog(@"There are %lu photos to Videofy", (unsigned long)self.photosToVideofy.count);

}

This works fine when the number of photos is less than 50. After that memory jumps to 150-160mb, I get the message Connection to assetsd was interrupted or assetsd died and the app crashes. How can I release the assets (PHFetchResult) from memory after I get the ones I want?(do i need to?) I would like to be able to add up to 150 photos. Any ideas? Thanks

Upvotes: 3

Views: 4842

Answers (4)

pw2
pw2

Reputation: 386

You have to set

photoRequestOptions.synchronous = NO;

instead of

photoRequestOptions.synchronous = YES;

Worked for me, iOS 10.2

Upvotes: 0

MadeByDouglas
MadeByDouglas

Reputation: 2835

You should not put the results from PHFetchResult into an Array. The idea of PHFetchResult is to point to many images from the Photos library without storing them all in RAM, (I'm not sure how exactly it does this) just use the PHFetchResult object like an array and it handles the memory issues for you. For example, connect a collectionViewController to the PHFetchResult object directly and use the PHImageManager to request images only for visible cells etc.

From apple documentation: "Unlike an NSArray object, however, a PHFetchResult object dynamically loads its contents from the Photos library as needed, providing optimal performance even when handling a large number of results." https://developer.apple.com/library/ios/documentation/Photos/Reference/PHFetchResult_Class/

Upvotes: 2

Anna Dickinson
Anna Dickinson

Reputation: 3347

This might be intentional (can't tell without looking at the rest of your code), but self.photosToVideofy is never released: since you're accessing it in a block, the object to which you pass the block ([PHImageManager defaultManager]) will always have a reference to the array.

Try explicitly clearing your array when you're done with the images. The array itself still won't be released, but the objects it contains will (or can be if they're not referenced anywhere else).

The best solution is to remove the array from the block. But, that would require changing the logic of your code.

Upvotes: 0

Artur Kucaj
Artur Kucaj

Reputation: 1081

Your code inside fetchImages method needs some refactoring, take a look on this suggestion:

-(void) fetchImages {

    PHFetchOptions *options = [[PHFetchOptions alloc] init];
    options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *assets = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];

    self.photosToVideofy = [[NSMutableArray alloc] init];

    CGSize size = CGSizeMake(640, 480);
    PHImageRequestOptions *photoRequestOptions = [[PHImageRequestOptions alloc] init];
    photoRequestOptions.synchronous = YES;

    for (NSInteger i = self.firstPhotoIndex; i < self.lastPhotoIndex; i++)
    {
        PHAsset *asset = assets[i];
        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFit options:photoRequestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
            if (result) {
                [self.photosToVideofy addObject:result];


            }
        }];
    }
    NSLog(@"There are %lu photos to Videofy", (unsigned long)self.photosToVideofy.count);
}

But the problem is memory consumption. Lets make some calculations.

Single image, using ARGB and 4 bytes per pixel:

640x480x4 = 1.2MB

And now, you want to store in the RAM 150 images, so:

150x1.2MB = 180MB

For example, iPhone 4 with 512 MB will crash if you use more that about 300 MB, but it can be less if other apps are also consuming a lot of RAM.

I think, you should consider storing images to files instead to RAM.

Upvotes: 1

Related Questions