使用照片框架请求照片时出现内存泄漏问题

3

我正在使用以下方法请求多张照片并将它们添加到数组中以供稍后使用:

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

}

当照片数少于50张时,一切正常。但是当照片数量超过50张时,内存占用会跳到150-160MB,然后出现错误信息连接到assetsd被中断或assetsd已停止运行,应用程序崩溃。 获取所需的资源(PHFetchResult)后,如何释放内存?(是否需要这样做?) 我想添加最多150张照片。 有什么思路吗? 谢谢。

4个回答

2
您不应该将PHFetchResult的结果放入数组中。PHFetchResult的想法是指向来自照片库的许多图像,而不将它们全部存储在RAM中(我不确定它是如何做到的),只需像使用数组一样使用PHFetchResult对象即可,它会为您处理内存问题。例如,直接将collectionViewController连接到PHFetchResult对象,并使用PHImageManager仅请求可见单元格的图像等。
从苹果文档中得知: “但与NSArray对象不同,PHFetchResult对象会根据需要动态从照片库加载其内容,即使处理大量结果时也提供最佳性能。” https://developer.apple.com/library/ios/documentation/Photos/Reference/PHFetchResult_Class/

获取照片时,我同意您不需要使用资产数组,但是当实际请求图像时,您必须将它们存储在某个地方... - Frederic Adda

1

您的fetchImages方法中的代码需要进行一些重构,请参考以下建议:

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

但问题在于内存消耗。让我们进行一些计算。
单个图像,使用ARGB和每像素4字节:
640x480x4 = 1.2MB

现在,您想将150张图像存储在RAM中,因此:

150x1.2MB = 180MB

例如,512 MB的iPhone 4如果使用超过约300 MB,将会崩溃,但如果其他应用程序也消耗了大量RAM,则可能会更少。 我认为,您应该考虑将图像存储到文件而不是RAM中。

谢谢您的回复。 - Kaitis
你应该考虑将图片存储到文件中,而不是存储到内存中。这句话对我帮助很大。事实上,当请求视频时,我们得到的是视频的URL,而不是视频本身;并且内存问题也不会发生。因此,我将对图片采取同样的做法:请求图片,然后立即将它们存储到缓存目录中的磁盘上,并传递一个URL给需要访问图片的人。 - Frederic Adda

0

这可能是有意为之的(没有看到您的其余代码就无法确定),但 self.photosToVideofy 从未被释放:因为您在块中访问它,所以传递给块的对象([PHImageManager defaultManager])将始终引用该数组。

尝试在完成图像处理后显式地清除数组。数组本身仍然不会被释放,但其中包含的对象将会被释放(如果它们没有被其他地方引用)。

最好的解决方案是从块中删除数组。但是,这需要更改您代码的逻辑。


嗨,安娜!我看到了你的回答。我猜这可能与保留/循环有关?我想问一下,在Swift中,这是否也相关?如果是这样,那么将myArrayOfWhatEver = nil,应该可以清除内存吗?谢谢! - Roi Mulia

0

你需要设置

photoRequestOptions.synchronous = NO;

替代

photoRequestOptions.synchronous = YES;

对我有用,iOS 10.2


1
这与所请求的照片存储方式无关。 - Frederic Adda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接