以前在项目中遇到了加载高清大图的场景,处理很差占用大量内存,形成性能问题,影响用户体验。ios
如何去解决加载高清大图这个难点呢?先看看iOS中的图片加载流程。git
国外有文章对图片的工做流写的很清楚,这里是连接。github
简单来讲是三步:bash
性能问题的通常是发生在解码图片这个步骤上,SDWebImage是提早强解码图片,这样在渲染时就不会再发生解码,其强解码的代码是在SDWebImageCoderHelper
中的decodedImageWithImage
函数中CGImageRef imageRef = [self CGImageCreateDecoded:image.CGImage];
。app
先说结论:不能。ide
测试SDWebImage的代码以下:函数
imageView.sd_setImage(with: url!) { (image, error, cacheType, theUrl) in
print(image?.size)
}
复制代码
使用Instruments能够看出SDWebImage的内存占用很是大,加载测试图
这样图片内存占用达到270MB,峰值内存达到800MB+。性能
能够看出,SDWebImage没法解决高清图内存占用太高和内存峰值太高的问题。测试
SDWebImage加载高清图的性能问题,主要是其直接加载高清图的原来尺寸致使的,而加载一张图片的内存占用 = 图片Width * 图片Height * 4
。因此直接加载测试图
的原始分辨率的内存占用是 7033 * 10100 * 4 / (1024 * 1024) = 约270MB
。优化
所以,对于高清图的加载,须要采用缩减分辨率的方法来减轻加载的内存压力,这里使用DownSampling
的方法,具体的方法介绍看WWDC2018-最佳图像实践这个Session。
DownSampling的具体原理是,在图像解码以前加入建立缩略图的过程,对加载的Image进行预处理,减小解码后的Image Buffer的大小,从而减小加载的内存占用和内存峰值。
这是DownSampling的示例代码:
func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
//生成CGImageSourceRef 时,不须要先解码。
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
//在建立Thumbnail时直接解码
let downsampleOptions = [kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary
//生成UIImage,强制解码
let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)!
return UIImage(cgImage: downsampledImage)
}
复制代码
这是使用DownSampling优化后的Instrument内存监测:
加载方式 | 内存占用 | 内存峰值 |
---|---|---|
SDWebImage | 270MB | 800MB+ |
DownSampling | 14MB | 40MB |
从对比中能够看出,使用DownSampling能大大下降内存的占用和峰值。
对于高清大图的场景来讲,内存的占用尤其重要,如何下降图片占用的内存,对提高用户体验有着相当重要的做用。从上面的对比中能够看出,使用SDWebImage是难以应对高清大图这种场景的,而DownSampling这种缩略图的方案却能很好适应这种场景。