内存泄漏-UIImagePNGRepresentation [英] Memory Leak - UIImagePNGRepresentation

查看:181
本文介绍了内存泄漏-UIImagePNGRepresentation的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将图像从UIImagePicker复制到文档目录.我正在使用@"UIImagePickerControllerOriginalImage"键从UIImagePickerDelegate字典中获取原始图像.我正在使用UIImagePNGRepresentation将图像写入文件.当我添加(重复该过程)具有高分辨率(图像大小约为20 mb)的图像时,我遇到了内存问题.

I am trying to copy an image from UIImagePicker to documents directory. I am using @"UIImagePickerControllerOriginalImage" key to get the original image from the dictionary of UIImagePickerDelegate. I am writing the image to file using UIImagePNGRepresentation. When I am adding(repeating the process) images with high resolution(image size approx 20 mb) I am facing memory issues.

我分析并使用了Xcode的内存泄漏功能,并放大了以下导致泄漏的代码.

I profiled and used the memory leak feature of the Xcode and it zoomed in on the following piece of code, which is responsible for the leakage.

@autoreleasepool {
    imagesData = UIImagePNGRepresentation(images);
    [imagesData writeToFile:name atomically:NO];
    imagesData = nil;
    //[UIImageJPEGRepresentation(images, 1.0) writeToFile:name atomically:YES];
}

我在这里看到了许多有关UIImagePNGRepresentation引起的内存泄漏的问题.但是我还没有找到解决我问题的合适方法.需要帮助.

I have seen many questions here, regarding memory leaks caused by UIImagePNGRepresentation. But I haven't found a proper solution to my problem. Need help.

推荐答案

我不知道UIImagePNGRepresentation有任何泄漏",但这肯定是对内存的过度使用,但是这里有两个问题:

I'm unaware of any "leak" with UIImagePNGRepresentation, but it certainly is an extravagant use of memory, but there are a couple of issues here:

  1. 首先,通过UIImage对原始资产进行往返处理,然后使用UIImagePNGRepresentation()的过程效率很低,最终可能会产生比原始资产大得多的NSData.例如,我选择了一张原始资产为1.5mb,UIImageJPEGRepresentation(compressionQuality为1.0)为6mb,UIImagePNGRepresentation()约为10mb的照片. (这些数字在图像之间可能会发生很大变化,但是您有了基本的想法.)

  1. First, the process of round-tripping the original asset through a UIImage and then using UIImagePNGRepresentation() is fairly inefficient and can end up with a NSData that is considerably larger than the original asset. For example, I picked a photo whose original asset 1.5mb, theUIImageJPEGRepresentation (with compressionQuality was 1.0) was 6mb, and the UIImagePNGRepresentation() was about 10mb. (These numbers can change quite a bit from image to image, but you get the basic idea.)

您通常可以通过使用compressionQuality小于<1.0的UIImageJPEGRepresentation来缓解此问题(例如0.8或0.9可以提供最小的图像质量损失,但可以观察到的NSData位置减少).但这是有损压缩.此外,在此过程中,您会丢失一些图像元数据.

You can often mitigate this problem by using UIImageJPEGRepresentation with a compressionQuality of less than 1.0 (e.g. 0.8 or 0.9 offers minimal image quality loss, but yet an observable reduction in NSData site). But this is a lossy compression. Furthermore, you lose some image meta data in this process.

我相信您正在同一时间将同一图像的多个副本保存在内存中:您同时具有UIImage表示形式和NSData对象.

I believe you are holding multiple copies of the same image in memory at the same time: You have both the UIImage representation as well as the NSData object.

不仅资产的NSData表示形式大于其所需的大小,而且还一次将整个资产加载到内存中.没必要.

Not only is the NSData representation of the asset larger than it needs to be, you're also loading the entire asset into memory at one time. This is not necessary.

您可能会考虑将原始资产从ALAssetLibrary直接流式传输到持久性内存,而不使用UIImagePNGRepresentationUIImageJPEGRepresentation,也完全不将其加载到UIImage中.而是创建一个小缓冲区,通过getBytes重复用部分原始资产填充此缓冲区,并在使用过程中使用NSOutputStream将此小缓冲区写入临时文件.您可以重复该过程,直到将整个资产写入持久性存储中.此过程的总内存占用量比替代方法要低得多.

You might consider, instead, streaming the original asset from the ALAssetLibrary directly to persistent memory, without using UIImagePNGRepresentation or UIImageJPEGRepresentation, and without loading it into a UIImage at all. Instead, create a small buffer, repeatedly filling this buffer with portions of the original asset via getBytes, writing this small buffer to a temporary file using NSOutputStream as you go along. You can repeat that process until the entire asset is written to persistent storage. The total memory footprint of this process is much lower than the alternative approaches.

例如:

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;

        NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
        NSString *path = [documentsPath stringByAppendingPathComponent:filename];
        NSString *tempPath = [self pathForTemporaryFileWithPrefix:@"ALAssetDownload"];

        NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:tempPath append:NO];
        NSAssert(outputStream, @"Unable to create output stream");

        [outputStream open];

        long long representationOffset = 0ll;
        NSError *error;

        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);
                [outputStream close];
                [[NSFileManager defaultManager] removeItemAtPath:tempPath error:nil];
                return;
            } else {
                remaining -= bytesRetrieved;
                representationOffset += bytesRetrieved;
                [outputStream write:buffer maxLength:bytesRetrieved];
            }
        }

        [outputStream close];

        if (![[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:path error:&error]) {
            NSLog(@"Unable to move file: %@", error);
        }

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

- (NSString *)pathForTemporaryFileWithPrefix:(NSString *)prefix
{
    NSString    *uuidString = [[NSUUID UUID] UUIDString];

    // If supporting iOS versions prior to 6.0, you can use:
    //
    // CFUUIDRef uuid = CFUUIDCreate(NULL);
    // assert(uuid != NULL);
    // NSString *uuidString = CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
    // CFRelease(uuid);

    return [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@", prefix, uuidString]];
}

这篇关于内存泄漏-UIImagePNGRepresentation的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆