首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >内存泄漏- UIImagePNGRepresentation

内存泄漏- UIImagePNGRepresentation
EN

Stack Overflow用户
提问于 2014-08-15 06:28:25
回答 2查看 4.3K关注 0票数 3

我正在尝试将图像从UIImagePicker复制到documents目录。我使用@"UIImagePickerControllerOriginalImage"键从UIImagePickerDelegate字典中获取原始图像。我正在使用UIImagePNGRepresentation将图像写入文件。当我添加(重复处理)高分辨率图像(图像大小约为20 mb)时,我将面临内存问题。

我分析并使用了Xcode的内存泄漏特性,它放大了下面的代码,这是造成泄漏的原因。

代码语言:javascript
复制
@autoreleasepool {
    imagesData = UIImagePNGRepresentation(images);
    [imagesData writeToFile:name atomically:NO];
    imagesData = nil;
    //[UIImageJPEGRepresentation(images, 1.0) writeToFile:name atomically:YES];
}

我在这里看到了许多关于UIImagePNGRepresentation造成的内存泄漏的问题。但我还没有找到解决问题的适当办法。需要帮助。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-08-15 06:58:47

我不知道UIImagePNGRepresentation有什么“泄漏”,但这肯定是对内存的过度使用,但这里有几个问题:

  1. 首先,通过UIImage往返原始资产,然后使用UIImagePNGRepresentation()的过程效率很低,最终可能会产生一个比原始资产大得多的NSData。例如,我选择了一张照片,它的原始资产为1.5mb,UIImageJPEGRepresentation( compressionQuality为1.0)为6mb,UIImagePNGRepresentation()约为10 6mb。(这些数字在图像之间可能会发生很大的变化,但你有了基本的想法。) 您通常可以通过使用小于1.0的UIImageJPEGRepresentation (例如,0.8或0.9提供最小的图像质量损失,但在NSData站点上却明显减少)来缓解这个问题。但这是有损压缩。此外,在此过程中,您将丢失一些图像元数据。

  1. 我相信您同时在内存中保存同一个映像的多个副本:您有UIImage表示和NSData对象。

  1. 不仅资产的NSData表示比它所需要的要大,您还同时将整个资产加载到内存中。这是不必要的。

相反,您可以考虑将原始资产直接从ALAssetLibrary流到持久内存,而不使用UIImagePNGRepresentationUIImageJPEGRepresentation,也根本不将其加载到UIImage中。相反,创建一个小缓冲区,通过getBytes重复地用原始资产的一部分填充这个缓冲区,在执行过程中使用NSOutputStream将这个小缓冲区写入一个临时文件。您可以重复该过程,直到将整个资产写入持久存储。该进程的总内存占用量比其他方法要低得多。

例如:

代码语言:javascript
复制
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]];
}
票数 5
EN

Stack Overflow用户

发布于 2016-12-22 02:21:57

我通过发送4通道映像(RGBA或RGBX)来解决这个问题,而不是3通道映像(RGB)。您可以检查是否有任何机会改变您的图像参数。

使用kCGImageAlphaNoneSkipLast而不是kCGImageAlphaNone

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25322138

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档