4

使用 UIImageJPEGRepresentation 时收到内存警告,有什么方法可以避免这种情况吗?它不会使应用程序崩溃,但如果可能的话,我想避免它。它间歇性地不运行[[UIApplication sharedApplication] openURL:url];

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
    NSData *imageToUpload = UIImageJPEGRepresentation(image, 1.0);

    // code that sends the image to a web service (omitted)
    // on success from the service
    // this sometime does not get run, I assume it has to do with the memory warning?
    [[UIApplication sharedApplication] openURL:url];
}
4

3 回答 3

5

使用UIImageJPEGRepresentation(您通过 a 来回往返资产UIImage)可能会出现问题,因为使用compressionQuality1.0 的 a,结果NSData实际上可能比原始文件大得多。(另外,您在 . 中持有该图像的第二个副本UIImage。)

例如,我刚刚从 iPhone 的照片库中随机选择了一张图片,原始资源为 1.5mb,但a为 1.0 的NSData制作者需要 6.2mb。并且将图像保存在本身可能会占用更多内存(因为如果未压缩,它可能需要,例如,每个像素四个字节)。UIImageJPEGRepresentationcompressionQualityUIImage

相反,您可以使用以下方法获取原始资产getBytes

static NSInteger kBufferSize = 1024 * 10;

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSURL *url = info[UIImagePickerControllerReferenceURL];

    [self.library assetForURL:url resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *representation = [asset defaultRepresentation];
        long long remaining = representation.size;
        NSString *filename  = representation.filename;

        long long representationOffset = 0ll;
        NSError *error;
        NSMutableData *data = [NSMutableData data];

        uint8_t buffer[kBufferSize];

        while (remaining > 0ll) {
            NSInteger bytesRetrieved = [representation getBytes:buffer fromOffset:representationOffset length:sizeof(buffer) error:&error];
            if (bytesRetrieved <= 0) {
                NSLog(@"failed getBytes: %@", error);
                return;
            } else {
                remaining -= bytesRetrieved;
                representationOffset += bytesRetrieved;
                [data appendBytes:buffer length:bytesRetrieved];
            }
        }

        // you can now use the `NSData`

    } failureBlock:^(NSError *error) {
        NSLog(@"assetForURL error = %@", error);
    }];
}

这避免了在 a 中暂存图像,UIImage并且结果NSData可以(对于照片,无论如何)要小得多。请注意,这还有一个优点,即它也保留了与图像关联的元数据。

顺便说一句,虽然上面的内容代表了显着的内存改进,但您可能会看到更显着的内存减少机会:具体来说,与其NSData一次将整个资产加载到 a 中,您现在可以流式传输资产(NSInputStream使用此getBytes例程的子类在需要时获取字节,而不是一次将整个内容加载到内存中)。这个过程有一些麻烦(请参阅BJ Homer 关于该主题的文章),但如果您正在寻找显着减少内存占用的方法,那就是这样。这里有几种方法(BJ,使用一些暂存文件并从中流式传输等),但关键是流式传输可以显着减少您的内存占用。

但是通过避免UIImagein UIImageJPEGRepresentation(这避免了图像占用的内存以及产生的较大内存NSDataUIImageJPEGRepresentation,您可能会取得相当大的进展。此外,您可能希望确保一次在内存中没有此图像数据的冗余副本(例如,不要将图像数据加载到 aNSData中,然后NSDataHTTPBody...可以一举搞定)。如果情况变得更糟,您可以采用流媒体方法。

于 2014-08-11T20:34:23.583 回答
4

在 ARC 中:只需将您的代码放在 @autoreleasepool 的小块中

 @autoreleasepool {
     NSData *data = UIImageJPEGRepresentation(img, 0.5);
     // something with data
}
于 2016-07-29T15:06:50.273 回答
1

呈现为格式和图像的答案。

使用仪器检查由于保留但未泄漏的内存而导致的泄漏和内存丢失。后者是仍然指向的未使用内存。在 Instruments 上的 Allocations 工具中使用标记生成(Heapshot)。

有关如何使用 Heapshot 查找内存占用,请参阅:bbum 博客

基本上,该方法是运行 Instruments allocate 工具,获取一个 heapshot,运行代码的迭代并获取另一个 heapshot,重复 3 或 4 次。这将指示在迭代期间已分配但未释放的内存。

要弄清楚结果,请查看个人分配。

如果您需要查看对象使用工具的保留、释放和自动释放发生的位置:

在仪器中运行,在分配中设置“记录引用计数”(对于 Xcode 5 及更低版本,您必须停止记录以设置选项)。使应用程序运行、停止记录、向下钻取,您将能够看到所有保留、释放和自动释放发生的位置。

于 2014-08-11T17:38:48.493 回答