为什么在Swift5中,UIImage.jpegData()调用会保留这么多内存?
我有一个运行以下代码段的应用程序-- 观察结果是,在以下代码的步骤中,我将看到一个内存尖峰为什么在Swift5中,UIImage.jpegData()调用会保留这么多内存?,swift,jpeg,swift5,Swift,Jpeg,Swift5,我有一个运行以下代码段的应用程序-- 观察结果是,在以下代码的步骤中,我将看到一个内存尖峰 let imageData = outputImage.jpegData(compressionQuality: 1) 在整个视图被释放之前,内存峰值不会消失。我确实尝试过一些autoreleasepool()或其他此处建议的put-in DispatchQueue()操作,但显然它们不起作用 为了减少混淆,generateImage()调用不会产生内存泄漏,而只会产生峰值,即generateImage
let imageData = outputImage.jpegData(compressionQuality: 1)
在整个视图被释放之前,内存峰值不会消失。我确实尝试过一些autoreleasepool()或其他此处建议的put-in DispatchQueue()操作,但显然它们不起作用
为了减少混淆,generateImage()调用不会产生内存泄漏,而只会产生峰值,即generateImage()调用后内存立即下降,但我只是将其放在那里,看看这是否相关(我尝试了其他UIImage与jpegData()的关系,但我无法100%再现此问题)
我不太愿意把它称为内存泄漏,因为我相信这个对象(实际上我不知道为什么UIImage.jpegData()调用也消耗了那么多内存,看起来像是Swift内存管理算法的一个bug)最终会在卸载视图时被回收。但是有没有办法避免JPEG数据转换造成的巨大内存消耗或释放内存
(使用此UIImage.jpegData()操作,在我的iPhone上加载4800*60000像素的24MB图像时,内存将增加约100MB)。4800*6000=28.8M,每像素4字节(RGBA)=1.15亿字节。因此~100MB对于未压缩的数据听起来完全正确,这正是您在
generateImage
中创建的数据。CoreGraphics试图在这些事情上偷懒,因此在您强制它渲染所有内容之前(当您要求jpegData
时),您可能看不到实际成本
您编写的代码实际上没有意义(您从未定义outputImage
或使用newImage
),因此我们不得不猜测很多。您可能正在将新图像
或输出图像
存储在某个位置,而没有向我们显示。可能在属性中,该属性将匹配“当此视图被释放时”
但您似乎也在使用imageView
显示此图像。如果是这样的话,它将消耗大约100MB的内存,因为它必须解压缩它才能在屏幕上显示。因此,很可能您只是查看图像视图的内存使用情况,这也与“当此视图被释放时”匹配。如果是这种情况,我预计内存使用情况会增加一倍以上(因为您也渲染它,然后压缩它)。但是你可能看不到这个尖峰,因为当你处理完它的时候,内存会被释放,只剩下100MB用于图像视图
(这是“100MB用于图像视图”完全取决于您设置图像视图的方式。如果它直接从文件中读取,则可能会有内存映射压缩数据,并且只应渲染到视图的实际大小。UIImageView非常智能。但是显示大图像总是需要大量内存。最终,您需要字节。)
这段代码试图做什么还不清楚。我怀疑可以更有效地完成(鉴于您似乎已经有了图像视图,我不确定为什么要进行渲染;您应该能够直接操作底层图像)
但最终,如果你想要一个4800*6000像素的RGBA图像,那就需要大约100MB的工作内存来操作。这就是大图像的成本。如果内存太多,则需要减少正在处理的像素数。有趣的是,下面的代码解决了这个问题,显然这个问题来自generateImage()方法,但我无法解释原因
func generateImage() -> Data {
// NOTE do not put any of these codes out in case of retain issues of
// the JPEG data or the images.
let canvasSize = self.outputImage.size
UIGraphicsBeginImageContext(canvasSize)
self.imageView.zoomView?.layer.render(in: UIGraphicsGetCurrentContext()!)
let newImage = UIGraphicsGetImageFromCurrentImageContext()?.jpegData(compressionQuality: 0)
UIGraphicsEndImageContext()
return newImage!
}
我更新了此方法以直接返回数据,而不是返回UIImage,幸运的是,通过此更新,在调用将数据放入某些类变量/缓存后,用于将UIImage转换为JPEG数据(如Rob所述)的内存没有保留。但我还是不太明白其中的原因。也许人们可以发表更多评论?请澄清您的代码片段与“视图”之间的关系。上面的代码是UIView的子类吗?不要显示“代码片段”。在上下文中向我们展示实际代码。我们不知道你的代码是如何运行的,也不知道图像从哪里来,数据从哪里去。谢谢Rob。但是,您所指的“未压缩数据”是指用于处理JPEG的临时变量吗?为什么UIImage.jpegData()之后会保留此内存?为了显示图像(即在图像视图中),需要一份未压缩的副本。但我的另一点是,您没有给我们提供太多的代码,您可能正在将
generateImage
的结果存储在属性中,在这种情况下不会发布。这就是答案吗?或者你是在问问题的延伸?什么是generateMasicImage
?这将在未压缩的数据返回之前释放它,而不是返回未压缩的数据。根据您对返回值所做的操作,这可能非常有意义。很难说,因为您没有显示大多数重要的代码,而且您发布的代码与您描述的代码不太匹配,所以我们猜测了很多。例如,当您更改generateImage()
时,您显然也更改了调用代码,因为签名不同。但你还没有向我们展示这种变化。
func generateImage() -> Data {
// NOTE do not put any of these codes out in case of retain issues of
// the JPEG data or the images.
let canvasSize = self.outputImage.size
UIGraphicsBeginImageContext(canvasSize)
self.imageView.zoomView?.layer.render(in: UIGraphicsGetCurrentContext()!)
let newImage = UIGraphicsGetImageFromCurrentImageContext()?.jpegData(compressionQuality: 0)
UIGraphicsEndImageContext()
return newImage!
}