内存泄漏 – UIImagePNGRepresentation

我想从UIImagePicker复制到文档目录的图像。 我正在使用@"UIImagePickerControllerOriginalImage"键从UIImagePickerDelegate的字典中获取原始图像。 我正在使用UIImagePNGRepresentation将图像写入文件。 当我添加(重复过程)图像的高分辨率(图像大小约20 MB),我正面临着内存问题。

我分析并使用了Xcode的内存泄漏function,并放大了下面一段代码,这是泄漏的责任。

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

我在这里看到了很多关于UIImagePNGRepresentation引起的内存泄漏的问题。 但是我还没有find妥善解决我的问题。 需要帮忙。

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

  1. 首先,通过UIImage ,然后使用UIImagePNGRepresentation()UIImagePNGRepresentation()原始资产的过程相当低效,最终可能会导致NSData比原始资产大得多。 例如,我select了原始资产为1.5MB的照片, UIImageJPEGRepresentationcompressionQuality为1.0)为6mb, UIImagePNGRepresentation()大约为10mb。 (这些数字在图像之间可能会发生很大的变化,但是您可以获得基本的想法。)

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

  2. 我相信你是在同一时间内存同一个图像的多个副本:你有UIImage表示以及NSData对象。

  3. 不仅资产的NSData表示大于它所需要的值,还会一次将整个资产加载到内存中。 这不是必须的。

您可能会考虑将原始资产直接从ALAssetLibrarystream式传输到持久性内存,而不使用UIImagePNGRepresentationUIImageJPEGRepresentation ,也无需将其加载到UIImage中。 相反,创build一个小缓冲区,通过getBytes反复填充原始资源的一部分缓冲区,随着您的使用,使用NSOutputStream将这个小缓冲区写入临时文件。 您可以重复该过程,直到整个资产被写入持久性存储。 这个过程的总内存占用量远低于其他方法。

例如:

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

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

使用kCGImageAlphaNoneSkipLast而不是kCGImageAlphaNone