图片体积(size)指的是图片文件占用的存储空间的大小。
数字图片存储的时候如果不压缩体积会比较大。比如,一张 1920x1080 的 24 位彩色 BMP 图片的体积约为 6MB。压缩可以减小体积。压缩分有损(lossy)(常见如 JPEG 格式)和无损(lossless)(常见如 PNG 格式)两种。
无损压缩不会丢失图片的任何信息,而仅仅是通过减少重复达到缩小体积的目的。有损图片压缩技术利用人眼的特性,使得可以将部分图片细节丢掉而人眼无法区分(或者说区别不明显)一般而言,对于同一张原始图片,有损压缩得越厉害,得到的压缩后的图片偏离原始图片就越大,质量越低。
图片体积和尺寸、质量的关系比较复杂,因为涉及到图片内容的特性复杂图片更难压缩、简单图像更容易压缩)、压缩方法等多种因素的影响。通常可以这么理解:尺寸越大、质量越高,则体积越大。
问题一:图像展示有这么多细节在里面,可是为什么在平常开发中为什么没有感觉到,可以从哪些地方对自己的工程进行优化。
答:我们平常大部分会使用UIImage imageNamed这样的API加载了本地图片,而网络图片则使用了SDWebImage或者YYWebImage等框架来加载。所以没有去细究。
问题二: 使用imageNamed,系统何时去解码,有没有缓存,缓存的大小是多少,有没有性能问题,和imageWithContentsOfFile有什么区别
答: 一一来解答这个问题
首先先说imageNamed和imageWithContentsOfFile有什么区别,想必大部分小伙伴都很清楚,因为这也是面试老生常谈的东西。imageNamed加载本地图片会缓存图片,也就是加载一千张相同的本地图片,内存中也只会有一份,而imageWithContentsOfFile不会缓存,也就是重复加载相同图片,在内存中会有多份图片数据。
imageNamed加载图片会将图片源数据和解码后的数据加载入内存缓存中,只有收到内存警告的时候才会释放,有兴趣的小伙伴可以自行调试一下。
对于 iOS 系统而言,绝大部分场景下哪类数据占内存最多呢?当然是图片!需要注意的是,图片所占内存的大小与图片的尺寸有关,而不是图片的文件大小。
例如:有一个 590KB 的图片,分辨率是 2048px * 1536px,它实际使用的内存不是 590KB,而是2048 * 1536 * 4 = 12 MB。。
当你缩小一幅图像的时候,会按照取平均值的办法把多个像素点变成一个像素点,这个过程称为 Downsampling。
1. 图像渲染管线 (Image Rendering Pipeline)
从 MVC 架构的角度来说,UIImage 代表了 Model,UIImageView 代表了 View. 那么渲染的过程我们可以这样很简单的表示:
Model 负责加载数据,View 负责展示数据。
但实际上,渲染的流程还有一个很重要的步骤:解码(Decode)。
为了了解Decode,首先我们需要了解Buffer这个概念。
2. 缓冲区 (Buffers)
Buffer在计算机科学中,通常被定义为一段连续的内存,作为某种元素的队列来使用。
下面让我们来了解几种不同类型的 Buffer。
Image Buffers代表了图片(Image)在内存中的表示。每个元素代表一个像素点的颜色,Buffer 大小与图像大小成正比.
The frame buffer 代表了一帧在内存中的表示。
Data Buffers代表了图片文件(Image file)在内存中的表示。这是图片的元数据,不同格式的图片文件有不同的编码格式。Data Buffers不直接描述像素点。 因此,Decode这一流程的引入,正是为了将Data Buffers转换为真正代表像素点的Image Buffer
3. 解码(Decoding)
将Data Buffers解码到Image Buffers是一个CPU密集型的操作。同时它的大小是和与原始图像大小成比例,和 View 的大小无关。
想象一下,如果一个浏览照片的应用展示多张照片时,没有经过任何处理,就直接读取图片,然后来展示。那 Decode 时,将会占用极大的内存和 CPU。而我们展示的图片的 View 的大小,其实是完全用不到这么大的原始图像的。
如何解决这种问题呢? 我们可以通过 Downsampling 来解决,也即是生成缩略图的方式。
通过Downsampling,我们成功地减低了内存的使用,但是解码同样会耗费大量的 CPU 资源。如果用户快速滑动界面,很有可能因为解码而造成卡顿。
UITableViewDataSourcePrefetching
解决办法:Prefetching+Background decoding
Prefetch 是 iOS10 之后加入到 TableView 和 CollectionView 的新技术。我们可以通过tableView(_:prefetchRowsAt:)这样的接口提前准备好数据。有兴趣的小伙伴可以搜一下相关知识。
至于Background decoding其实就是在子线程处理好解码的操作。
网友评论