10

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

目前,我正在遍历所有资产并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++;
}

根据日志,我看到首先调用所有“开始请求图像 x”,然后调用所有完成块(“完成请求图像 x”)。我认为这可能会导致内存问题。确保在释放这些资源并移动到下一次迭代之前调用每次迭代的完成块可能会减少内存密集型。我怎样才能做到这一点?

4

3 回答 3

9

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
}
于 2015-10-22T06:47:17.210 回答
9

@Inder Kumar Rathore 技巧对我不起作用。所以我试着在这里阅读更多关于 PHImageManager的信息

我发现如果我从

- requestImageForAsset:targetSize:contentMode:options:resultHandler:

- requestImageDataForAsset:options:resultHandler:

我将收到具有相同尺寸 {5376, 2688} 的图像,但字节大小要小得多。这样内存问题就解决了。

希望这有帮助!

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

于 2016-08-29T09:34:43.103 回答
1

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

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

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

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

于 2017-09-20T19:59:37.497 回答