ios图像和图形最佳实践(二)

·  阅读 2065

没有前面进度的同学还是从(一)开始,否则会感觉比较突兀

ios图像和图形最佳实践(一)

..........

image.png

首先 我们创建一个CGImageSource对象

CGImageSourceCreate可以接受一个选项字典参数 我们这里要传递的重要选项参数是这个 ShouldCache标志

这就告诉了 Core Graphics框架 我们只是在创建一个对象来表示存储在该URL的文件中的信息

不要立即解码这个图像 只需创建一个表示它的对象

我们将需要来自此URL的信息

image.png

然后我们将在水平和垂直轴上进行计算

该计算基于期望的图片的大小 以及我们要渲染的像素和点的大小

这是以像素为单位的较大维度

计算这些信息 然后为我们的缩略图创建一个选项字典

这里列出了几个选项,你可以在文档中查看这些选项的具体含义

但非常重要的是,这个CacheImmediately选项 通过在这里传递这个选项 我们告诉 Core Graphics,当我要求你创建缩略图时,这就是你应该为我创建解码图像缓存的确切时刻

因此 我们可以确切的控制何时调用CPU来进行解码

image.png

接着 我们绘制缩略图 即我们拿到所返回的CGImage 将其打包在UIImage中 并从我们在此编写的辅助函数中返回

image.png

为了让你了解这项技术为我们节省的开销数量,我们在 ios图像和图形最佳实践(一) 里以一张 3840x2400的图片 不做优化 与 优化(使用下采样技术 - 只创建一个实际大小的图像缓存) 做了对比

你可以想象,这对于 一小块空间里显示大量可能很大的输入图像的app来说 有多么重要

我们用UICollectionView来实现这样的视图

因此我们为indexPath中 item实现了单元格, 同时我们使用之前写的辅助函数 将图像下采样使之缩小到 当单元格实际放在屏幕上时它们将要显示的大小

image.png

image.png

你认为这是一件很好的事情 对吧?

我们实际上减少了内存的使用量 而不是允许系统中存在这些大的内存区域

不幸的是 这并不能解决我们的另一个问题

这些问题在可滚动视图 比如表视图和集合视图中是很常见的

你在app中滚动页面,而在滚动过程中页面发生了粘连

image.png

这里发生的情况是 当我们滚动页面时 CPU相对比较空闲

或它所做的工作可以在显示硬件需要帧缓存的下一个副本之前完成

image.png

所以当帧缓存被更新时 我们能看到流动的效果 并且显示硬件能够及时获得新帧

但现在 我们即将显示另一行图像 在将单元格交回UICollectionView之前 我们要求 Core Graphics解码这些图像 这将花费很长的CPU时间 以至于我们不得不重新渲染帧缓存

但显示器硬件按固定的时间间隔运行

因此 从用户的角度来看 app像卡住了一样

在可滚动视图中进行解码 我们完成了对图像的解码 我们可以将单元格提供给UICollectionView 和以前一样 动画继续 继续之前的刚刚 存在一个粘连

现在除了这种行为明显的响应性后果之外 其对电池寿命有更细微的不利影响

image.png

因为iOS非常擅长管理当CPU需求相对平稳持续时,对电池的电量需求

我们在这里可以看到峰值

当新行即将进入滚动视图时 我们会看到较高的CPU使用率 然后再回到较低的水平

我们可以使用两种技术来平滑我们的CPU使用率

第一个是预取

image.png

如果你想知道更多关于预取的知识,可以文档查看CollectionView一览

这里预取思想是 预取允许CollectionView告知我们的数据源 它当前不需要一个单元格 但它将在不久的将来需要

因此如果你有任何工作要做,也许现在就可以提前开始

这允许我们随时间推移来分摊CPU使用率

因此我们减少了CPU使用率的峰值大小

image.png

image.png

image.png

image.png

image.png

滑动时,预先对不可见视图执行下采样,由于是异步执行,不会粘连,同时可以控制列表图片占用内存始终是可见的视图内存,不致于很长列表图片数据导致内存暴涨

我们可以使用的另一种技术是在后台执行工作

image.png

既然我们已经随时间分散了工作量 我们也可以将这些工作分散到可用的CPU上

image.png

这样做的效果是 你的app具有更强的响应能力 并且该设备具有更长的电池寿命

为了在这里进行实际数据演示 我们已经在数据源上实现了预取方法

它将会调用我们的helper函数 来生成我们将要在CollectionView单元格中显示的图片的下采样版本

它通过将工作分派到其中一个全局异步队列来完成此任务

很好 我们的工作正在后台任务中进行 这就是我们想要做的

但这里有一个潜在的缺陷 这是一个我们喜欢称之为线程爆炸的现象 当我们要求系统去做比CPU能够做的更过的工作时 就会发生这种情况

如果我们要显示大量的图像,比如同时显示6张或8张图片 但是我们在只有两个CPU的设备上运行 我们就不能一次完成所有这些工作 我们无法在不存在的CPU上进行并行处理

现在为了避免 在我们向一个全局队列中异步的分配任务时发生死锁 GCD将创建新线程来捕捉我们要求它所做的工作

然后CPU将花费大量的时间在这些线程之间进行切换 以便尝试在所有工作上取得我们要求操作系统为我们做的渐进式进展 在这些线程之间不停切换 会产生相当大的开销

如果有一个或多个CPU一次处理完图片 效果会更好

因此我们将借鉴 “现代化中心调度GCD用法” 提出的一项技术,我们要序列化一些工作

我们现在不是简单的将工作分派到全局异步队列之一 而是创建一个串行队列

image.png

并且在预取方法的实现中 我们异步的将工作分派到该队列 它的确意味着单个图像的加载可能要比以前晚才能开始取得进展

但这也意味着CPU将花费更少的时间在它可以做的小任务之间来回切换

我们显示的这些图像可能来自多个地方 他们可能是随app附带的 在这种情况下它们可以存储在图像素材中 或者可能存储在一个文件中 而不是我们的程序包中 或者它们可能来自网络 或者是在 App document 文档目录内一个文档中 也可能存储在缓存中

但是对于我们的app所附带的图片 苹果强烈建议我们使用图像素材来存储

这其中有很多原因

ios图像和图形最佳实践(三)

分类:
iOS
标签:
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改