您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > IOS

iOS性能优化之图片最佳实践

时间:2020-08-02 10:54:22  来源:  作者:

UIImage是用来处理图像数据的高级类, UIImageView 是 UIKit 提供的用于显示 UIImage 的类。若采用 MVC 模型进行类比, UIImage 可以看作模型对象( Model ), UIImageView 是一个视图( View )。它们都肩负着各自的职责:

UIImage负责加载图片内容, UIImageView 负责显示和渲染它。

iOS性能优化之图片最佳实践

 

这看似是一个简单的单向过程,但实际情况却复杂的多,因为渲染是一个连续的过程,而不是一次性事件。这里还有一个非常关键的隐藏阶段,对衡量 App 性能至关重要,这个阶段被称为解码。

iOS性能优化之图片最佳实践

 

图片解码

在讨论解码之前,先了解下缓冲区的概念。

缓冲区:是一块连续的内存区域,用来表示一系列元素组成的内存,这些元素具有相同的尺寸,并通常具有相同的内部结构。

图像缓冲区:它是一种特定缓冲区,它保存了某些图像在内存中的表示。此缓冲区的每个元素,描述了图像中每个像素的颜色和透明度。因此这个缓冲区在内存中的大小与它包含的图像大小成正比。

帧缓冲区:它保存了 app 中实际渲染后的输出。因此,当 app 更新其视图层次结构时, UIKit 将重新渲染 app 的窗口及其所有视图到帧缓冲区中。帧缓冲区中提供了每个像素的颜色信息,显示硬件降读取这些信息用来点亮显示器上对应的像素。

iOS性能优化之图片最佳实践

 

如果 app 中没有任何改变,则显示硬件会从帧缓冲区中取出上次看到的相同数据。但是如果改变了视图内容,UIKit会重新渲染内容,并将其放入帧缓冲区,下一次显示硬件从帧缓冲区读取内容时,就会获取到新的内容。

数据缓冲区:包含图像文件的数据缓冲区,通常以某些元数据开头,这些元数据描述了存储在数据缓冲区中的图像大小和图像数据本身。

下面看下图像渲染到帧缓冲区的详细过程:

iOS性能优化之图片最佳实践

 

这块区域将由图像视图进行渲染填充。我们已经为图像视图分配一个 UIImage,它有一个表示图像文件内容的数据缓冲区。我们需要用每个像素的数据来填充帧缓冲区,为了做到这一点,UIImage 将分配一个图像缓冲区,其大小等于包含在数据缓冲区中的图像大小,并执行称为解码的操作,这就是将 JPEG 或 PNG 或其它编码的图像数据转换为每个像素的图像信息。然后取决于我们图像视图的内容模式,当 UIKit 要求图像视图进行渲染时,它会将数据复制到帧缓冲区的过程中对来自图像缓冲区的数据进行复制和缩放。

解码阶段是 CPU 密集型的,特别是对于大型图像。因此,不是每次 UIKit 要求图像视图渲染时都执行一次这个过程。 UIImage 绑定在图像缓冲区上,所以它只执行一次这个过程。因此,在你的 app 中,对于每个被解码的图像,都可能会持续存在大量的内存分配,这种内存分配与输入的图像大小成正比,而与帧缓冲区中实际渲染的图像视图大小没有必然联系,这会对内存产生相当不利的后果。

减少 CPU 的使用率

我们可以使用一种称为向下采样的技术来实现这一目标。

我们可以通过这种下采样技术来节省一些内存。本质上,我们要做的就是捕捉该缩小操作,并将其放入缩略图的对象中,最终达到降低内存的目的,因为我们将有一个较小的解码图像缓冲区。

iOS性能优化之图片最佳实践

 

这样,我们设置了一个图像源,创建了一个缩略图,然后将解码缓冲区捕获到 UIImage 中,并将该 UIImage 分配给我们的图像视图。接下来我们就可以丢弃包含图片数据的数据缓冲区,最终结果就是我们的 app 中将具有一个更小的长期内存占用足迹。

下面看下如何使用代码来实现这一过程:

  • 首先,创建一个 CGImageSource 对象
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!

KCGImageSourceShouldCache 参数为 false,用来告诉 Core Graphic 框架我们只是在创建一个对象,来表示存储在该 URL 的文件中的信息,不要立即解码这个图像,只需要创建一个表示它的对象,我们需要来自此 URL 的文件信息。

  • 然后在水平和垂直轴上进行计算,该计算基于期望的图片大小以及我们要渲染的像素和点大小:
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let downsampleOptions = [kCGImageSourceCreateThumbnailFromImageAlways: true,
                         kCGImageSourceShouldCacheImmediately: true,
                         kCGImageSourceCreateThumbnailWithTransform: true,
                         kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary

这里也创建了一个缩略图选项的字典,最重要的是 CacheImmediately 这个选项,通过这个选项,告诉 Core Graphics,当我们要求你创建缩略图时,这就是你应该为我创建解码缓冲区的确切时刻。因此,我们可以确切的控制何时调用 CPU 来进行解码。

  • 最后,我们创建缩略图,即拿到返回的 CGImage 。
let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)

其完整代码如下:

func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
        let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
        let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!
        let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
        let downsampleOptions = [kCGImageSourceCreateThumbnailFromImageAlways: true,
                                 kCGImageSourceShouldCacheImmediately: true,
                                 kCGImageSourceCreateThumbnailWithTransform: true,
                                 kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary
        let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)
        return UIImage(cgImage: downsampledImage)
    }

在 UICollectionView 中的使用

我们可能会在创建单元格时,直接使用下采样技术来生成的图片,代码如下:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
    cell.layoutIfNeeded()
    let imageViewSize = cell.imageView.bounds.size
    let scale = collectionView.traitCollection.displayScale
    cell.imageView.image = downsample(imageAt: "", to: imageViewSize, scale: scale)
}

这样确实会减少内存的使用量,但这并不能解决我们的另一个问题。这些问题在可滚动的视图中是非常常见的。

当我们滚动页面时,CPU 相对比较空闲或它所做的工作可以在显示硬件需要帧缓冲的下一个副本之前完成,所以,当帧缓冲被更新时,我们能看到流畅的效果,并且显示硬件能及时获得新帧。

但是,如果我们将显示另一行图像,将单元格交回 UICollectionView 之前,我们要求 core Graphics 解码这些图像,这将会花费很长的 CPU 时间,以至于我们不得不重新渲染帧缓冲区,但显示器硬件按固定的时间间隔运行,因此,从用户的角度来看,app 好像卡住了一样。

这不仅会造成信息粘连,还会有明显的响应性后果,也对电池寿命有不利的影响。

iOS性能优化之图片最佳实践

 

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

第一个是预取,它的基本思想是:预取允许 UICollectionView 告知我们的数据源,它当前不需要一个单元格,但它在不久的将来需要,因此,如果你有任何工作要做,也许现在可以提前开始。这允许我们随时间的推移,分摊 CPU 的使用率,因此,我们减少了CPU 使用的峰值。

另一种技术是在后台执行工作,既然我们已经随时间分散了工作量,我们也可以将这些技术分散到可用的 CPU 上。

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

iOS性能优化之图片最佳实践

 

具体代码如下:

func collectionView(_ collectionView: UICollectionView,
prefetchItemsAt indexPaths: [IndexPath]) {
	// Asynchronously decode and downsample every image we are about to show
	for indexPath in indexPaths {
		DispatchQueue.global(qos: .userInitiated).async {
			let downsampledImage = downsample(images[indexPath.row])
			DispatchQueue.main.async { 
			 self.update(at: indexPath, with: downsampledImage)
			 }
		}
	}
 }

我们在全局兵法队列中来使用下采样技术,但这里有个潜在的缺陷,就是有可能会引起线程爆炸。当我们要求系统去做比 CPU 能够做的工作更多的工作时,就会发生这种情况。

为类避免线程爆炸,我们现在不是简单的将工作分配到全局异步队列中,而是创建一个串行队列,并且在预取方法的实现中,异步的将任务分配到该队列中,实现如下:

let serialQueue = DispatchQueue(label: "Decode queue") 
func collectionView(_ collectionView: UICollectionView,
prefetchItemsAt indexPaths: [IndexPath]) {
	// Asynchronously decode and downsample every image we are about to show
	for indexPath in indexPaths {
		serialQueue.async {
			let downsampledImage = downsample(images[indexPath.row])
			DispatchQueue.main.async { self.update(at: indexPath, with: downsampledImage)
		}
	}
 }


Tags:iOS   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
应用锁一直是苹果用户的痛点,毕竟这功能隔壁安卓早就有了。 为什么苹果一直不给上应用锁的功能呢?之前记者也有提问过苹果的前设计师(首席声优)艾维,他说在苹果的逻辑里,手机密码...【详细内容】
2021-12-23  Tags: iOS  点击:(20)  评论:(0)  加入收藏
众所周知,由于今年iOS 15系统的更新幅度不大,导致不少果粉的升级欲望大大降低,iOS 15系统的安装率也远低于去年的iOS 14系列,今年的iOS 15这么拉跨,那么明年的iOS 16呢? 目前关于i...【详细内容】
2021-12-22  Tags: iOS  点击:(9)  评论:(0)  加入收藏
IT之家 12 月 20 日消息,百度网盘青春版 iOS 客户端今日晚间率先开启内测,安卓客户端将在稍后内测。使用苹果 iPhone 的IT之家小伙伴可以点此下载内测版,需要先下载 TestFlight...【详细内容】
2021-12-21  Tags: iOS  点击:(9)  评论:(0)  加入收藏
相信很多小伙伴都遇到过这种情况,一台iPhone放太久了,当初设置的锁屏密码是什么都自己忘了。 这时候我们只能通过连接电脑进入恢复模式再进行刷机或者用另一台iPhone(登录的是...【详细内容】
2021-12-21  Tags: iOS  点击:(16)  评论:(0)  加入收藏
近日苹果发布 iOS 15.2/iPadOS 15.2 正式版的更新,整合了此前测试版中所有新功能,因此更新内容不少,也有一些值得了解的实用功能。有没有必要更新 iOS 15.2 正式版?看完如下功能...【详细内容】
2021-12-17  Tags: iOS  点击:(23)  评论:(0)  加入收藏
虽然手机圈每隔几天就会有新机登场,但依旧有小伙伴表示内心毫无波澜—— 「你发任你发,我的小米 6 还能再战两年。」 就算是电池损耗了,按键不灵了,屏幕摔碎了,修一修...【详细内容】
2021-12-16  Tags: iOS  点击:(7)  评论:(0)  加入收藏
苹果昨日向 iPhone 和 iPad 用户推送了 iOS 15.2 / iPadOS 15.2 正式版更新,带来了多项新功能。据 9To5Mac 报道,iOS 15.2 / iPadOS 15.2 还有一项新功能,允许用户不借助 Mac...【详细内容】
2021-12-16  Tags: iOS  点击:(13)  评论:(0)  加入收藏
今天发了几个关于iOS15.2正式版的微头条,没想到引发了大家不小的讨论,看来大家对于新版本还是有不少期待的,不少人今天早上睡醒之后就选择了升级,还有一些小伙伴一直在纠结要不...【详细内容】
2021-12-15  Tags: iOS  点击:(20)  评论:(0)  加入收藏
在今天凌晨,苹果正式上线了 iOS 15.2 正式版 以及 watchOS 8.3 的新版本,这个正式版其实我是一直比较期待的,因为在此前的开发者测试版本中,就一直在内测APP 隐私报告,数字遗产以...【详细内容】
2021-12-15  Tags: iOS  点击:(20)  评论:(0)  加入收藏
【手机中国新闻】随着互联网进一步深入到我们的生活,我们已经与数字化脱不开关系了,同时在网络上拥有很多对于我们而言有价值的事物。因此,有不少网友在思考,自己故去以后,网络上...【详细内容】
2021-12-14  Tags: iOS  点击:(9)  评论:(0)  加入收藏
▌简易百科推荐
前言最近对 WebRTC iOS 端源码进行了下载和编译,网上针对 WebRTC iOS 端的编译文章基本都是几年前的,有些地方已经不适用于最新版的 WebRTC 的编译,简单记录下载&编译的过程,以...【详细内容】
2021-11-10  anyRTC云平台    Tags:WebRTC iOS   点击:(38)  评论:(0)  加入收藏
IT之家 10 月 20 日消息,据 9to5 Mac 报道,继 iOS 15.1 RC、macOS Monterey RC 和其他更新发布后,苹果也向开发者提供了 Xcode 13.1 RC(候选版)。最新版本的 Xcode 现在可以在苹...【详细内容】
2021-10-20    IT之家  Tags:Xcode   点击:(87)  评论:(0)  加入收藏
IT之家 8 月 12 日消 苹果今日发布了一款新的开发者工具,旨在强制那些运行 iOS 15 和 iPadOS 15 的设备在使用不安全的 WiFi 网络或 WiFi 速度较慢时优先使用 5G 连接而不是...【详细内容】
2021-08-12    IT之家  Tags: iOS   点击:(151)  评论:(0)  加入收藏
IT之家4月7日消息 去年底,苹果公司要求应用商店所有第三方软件都必须增加 “隐私标签”以进一步保护消费者隐私安全。之后,谷歌旗下大量 iOS 软件长时间停止更新。而到了今年...【详细内容】
2021-04-07    IT之家  Tags:苹果隐私标签   点击:(309)  评论:(0)  加入收藏
你们好,这里是Seek思科,为你寻找苹果最新资讯、苹果隐藏技巧、苹果良心APP以及苹果优质配件。如果你需要这些,不妨点个关注。苹果已经官宣今年全球开发者大会将在北京时间6月8...【详细内容】
2021-04-02  Seek思科  今日头条  Tags:iOS15系统   点击:(368)  评论:(0)  加入收藏
Sideloadly安装unc0ver越狱 今天推荐比Altstore更简单的安装工具,Sideloadly 在QQ群文件下载最新版本安装, 然后直接打开,演示是Mac版本 Win是一模一样的, 不会出现报错2或...【详细内容】
2021-03-30  CydiaInstaller  今日头条  Tags:iOS自签名   点击:(1272)  评论:(0)  加入收藏
就在昨日,苹果对《App Store 审核指南》进行了2021年的首次重大更新。而此次更新主要是为了方便开发者为即将发布的OS版本中的新功能提供支持,更好地保护用户,并帮助开发者的Ap...【详细内容】
2021-02-03      Tags:App Store   点击:(205)  评论:(0)  加入收藏
不用不上架、不越狱、不要企业证书、永不掉签 只需一个h5网站地址 就能创建属于您自己的苹果IOS APP_多啦咪资源网无需越狱 无需企业证书 不用上架appStore 界面全屏 无广告...【详细内容】
2020-12-11      Tags:APP   点击:(177)  评论:(0)  加入收藏
iOS14支持把小组件添加到桌面,不过我们之前推荐的都是一些工具类的或者是桌面美化类的小组件,今天我们来看看目前有哪些我们常用的App已经支持把小组件添加到桌面了。 网易云...【详细内容】
2020-11-26      Tags:iOS   点击:(185)  评论:(0)  加入收藏
简介: 在平时的面试中,经常问到的高频面试题大概做了一个总结,希望能帮到你!一、如何绘制UIView?绘制一个UIView最灵活的方法就是由它自己完成绘制。实际上你不是绘制一个UIView...【详细内容】
2020-11-13      Tags:iOS   点击:(146)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条