高内存使用率循环遍历 PHAssets 并调用 requestImageForAsset

High memory usage looping through PHAssets and calling requestImageForAsset

我正在使用图像选择器库来允许用户从他们的照片库中 select 许多图像。它们作为 PHAssets 的数组返回。然后,我想将所有 PHAssets 转换为 UIImages 并将它们写入应用程序的存储空间。

目前,我正在遍历所有资产并同步调用 requestImageForAsset。我的问题是,当此循环处于 运行 时,内存使用量会出现令人难以置信的高峰值(对于 30 张图像,它会飙升至 130MB)。我想阻止这种情况。

这是我的代码:

for(PHAsset *asset in self.assets) {
        NSLog(@"started requesting image %i", i);
        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:[self imageRequestOptions] resultHandler:^(UIImage *image, NSDictionary *info) {
            dispatch_async(dispatch_get_main_queue(), ^{
                assetCount++;
                NSError *error = [info objectForKey:PHImageErrorKey];
                if (error) NSLog(@"Image request error: %@",error);
                else {
                    NSString *imagePath = [appDelegate.docsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%i.png",i]];
                    NSData *imageData = UIImagePNGRepresentation(image);
                    if(imageData) {
                        [imageData writeToFile:imagePath atomically:YES];
                        [self.imagesArray addObject:imagePath];
                    }
                    else {
                        NSLog(@"Couldn't write image data to file.");
                    }
                    [self checkAddComplete];
                    NSLog(@"finished requesting image %i", i);
                }
            });
        }];
    i++;
}

根据日志,我看到首先调用了所有 "starting requesting image x",然后是所有完成块 ("finished requesting image x")。我认为这可能会导致内存问题。确保在释放这些资源并移动到下一次迭代之前调用每次迭代的完成块可能会占用更少的内存。我该怎么做?

请使用autoreleasepool进行内存管理。

for(PHAsset *asset in self.assets) {
    // This autorelease pool seems good (a1)
    @autoreleasepool {
        NSLog(@"started requesting image %i", i);
        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:[self imageRequestOptions] resultHandler:^(UIImage *image, NSDictionary *info) {
            dispatch_async(dispatch_get_main_queue(), ^{
                //you can add autorelease pool here as well (a2)
                @autoreleasepool {
                    assetCount++;
                    NSError *error = [info objectForKey:PHImageErrorKey];
                    if (error) NSLog(@"Image request error: %@",error);
                    else {
                        NSString *imagePath = [appDelegate.docsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%i.png",i]];
                        NSData *imageData = UIImagePNGRepresentation(image);
                        if(imageData) {
                            [imageData writeToFile:imagePath atomically:YES];
                            [self.imagesArray addObject:imagePath];
                        }
                        else {
                            NSLog(@"Couldn't write image data to file.");
                        }
                        [self checkAddComplete];
                        NSLog(@"finished requesting image %i", i);
                    }
                } //a2 ends here
            });
        }];
        i++;
    } // a1 ends here
}

@Inder Kumar Rathore 技巧对我不起作用。 所以我试着阅读更多关于 PHImageManager here

我发现如果我从

切换

- requestImageForAsset:targetSize:contentMode:options:resultHandler:

- requestImageDataForAsset:options:resultHandler:

i 将接收具有相同尺寸 {5376, 2688} 的图像,但字节大小要小得多。所以内存问题解决了。

希望对您有所帮助!!

(注意:[UIImage imageWithData:imageData] 使用它来将 NSData 转换为 UIImage)

我用递归方法解决了它,确保完成块在下一次迭代之前完成。这样就可以用非常低的内存获取数千张照片。 这是它的作用:

给定相机胶卷中所选照片的​​索引数组

  1. 请求数组中的第一个对象。
  2. 在完成块中,删除数组中的第一个对象并再次调用相同的方法。
  3. 重复直到数组为空

这是代码。

- (void)processPhotos
{
  NSIndexPath *indexPath = [_selectedPhotosArray objectAtIndex:0];
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    PHAsset *asset = [_allPhotos objectAtIndex:indexPath.row];
    [self.imageManager requestImageForAsset:asset
                                 targetSize:PHImageManagerMaximumSize
                                contentMode:PHImageContentModeAspectFill
                                    options:self.imageRequestOptions
                              resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
      NSError *error = [info objectForKey:PHImageErrorKey];
      if (_selectedPhotosArray.count > 0) {
        [_selectedPhotosArray removeObjectAtIndex:0];
      }
      if (error) { 
        NSLog(@"[CameraRoll] Image request error: %@",error);
      } else {
        if (result != nil) {
          [self processImage:result];
        }
      }
      if (_selectedPhotosArray.count > 0) {
        // Recurring loop
        [self processPhotos];
      }
    }];
  });
}

在第一次调用此方法之前,请确保检查数组是否为空。