UIImageJPEGRepresentation收到内存警告

时间:2014-08-11 16:44:53

标签: ios memory-leaks uiimagejpegrepresentation

使用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];
}

3 个答案:

答案 0 :(得分:5)

使用UIImageJPEGRepresentation(您通过UIImage对资产进行往返)可能会有问题,因为使用compressionQuality为1.0,结果NSData可以实际上比原始文件大得多。 (另外,您在UIImage中持有图像的第二份副本。)

例如,我刚刚从iPhone的照片库中选择了一张随机图片,原始资源为1.5mb,但NSData生成的UIImageJPEGRepresentation只有compressionQuality 1.0需要6.2mb。将图像保存在UIImage本身可能会占用更多内存(因为如果未压缩,则每个像素可能需要四个字节)。

相反,您可以使用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);
    }];
}

这样可以避免在UIImage中暂存图像,并且生成的NSData可以(对于照片而言)相当小。注意,这也有一个优点,即它也保留了与图像相关的元数据。

顺便说一下,虽然上面代表了显着的内存改进,但您可能会看到更显着的内存减少机会:具体而言,您现在可以不再将整个资产一次性加载到NSData中。资产(子类NSInputStream使用此getBytes例程来获取所需的字节,而不是一次将整个内容加载到内存中。这个过程涉及一些烦恼(参见BJ Homer's article on the topic),但是如果你正在寻找内存占用的显着减少,那就是这样。这里有几种方法(BJ&#39; s,使用一些暂存文件和流式传输等),但关键是流式传输可以大大减少你的内存占用。

但是通过避免UIImage中的UIImageJPEGRepresentation(这会避免图像占用的内存以及NSData产生的较大UIImageJPEGRepresentation),您可以取得很大进展。此外,您可能希望确保一次不在内存中存储此图像数据的冗余副本(例如,不要将图像数据加载到NSData中,然后构建一个NSData的第二个HTTPBody ...看看你能否一举做到这一点。如果情况变得更糟,你可以采用流媒体方式。

答案 1 :(得分:3)

ARC中的

:只需将您的代码放在@autoreleasepool

的小块中
 @autoreleasepool {
     NSData *data = UIImageJPEGRepresentation(img, 0.5);
     // something with data
}

答案 2 :(得分:1)

作为格式和图像的答案。

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

如何使用“快照”查找内存褶皱,请参阅:bbum blog

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

要弄清楚披露的结果,以查看个别分配。

如果您需要查看对象使用仪器的保留,释放和自动释放的位置:

在仪器中运行,在分配中设置&#34;记录参考计数&#34; on(对于Xcode 5及更低版本,您必须停止录制以设置选项)。导致应用程序运行,停止录制,向下钻取,您将能够看到所有保留,发布和自动释放的位置。