不改一行代码,就极大提高对本地图片加载速度(对 Asset 的探讨)

2017年8月4日更新

根据这个 Session Optimizing I/O for Performance and Battery Life 的描述,使用 Asset 还有对启动优化的好处。

Asset Catalogs

10%的速度提升

如果你是被标题吸引进来的

可以直接跳到最后看结论,接下来是对 Asset 为什么能加快对本地图片加载速度的探讨。

为什么探讨 Asset 这个东西

由于项目中在刚启动的瞬间使用 "-[UIImage imageNamed:]" 会很慢,所以就打算探讨 "-[UIImage imageNamed:]" 的实现,但是过程中发现了使用 Asset 对于图片的加载有极大的提升,就去探讨 Asset 了。

使用 Time Profiler 探索实现

在使用 Time Profiler 调试 "-[UIImage imageNamed:]" 时候,发现它实际是调用 "-[UIImage imageNamed:inBundle:compatibleWithTraitCollection:]" 的,这时候萌发了一个想法是会不会根据指定 Bundle 的范围会加快加载图片的速度(理由是文件夹小了,减少索引次数)。

实验后发现,果然如此。把图片分散到指定 bundle 后的速度大大提升了。

这时候,有同事提示我使用 Asset 会不会加快,因为 WWDC 有提到过。所以我就去看了这集 Session,发现它是这样描述的。

发现使用 Asset 后速度也大大提升。到此为止就产生了探讨 Asset 这个东西的需求了。

符号断点跟踪步骤

这里就用两个图片来简单描述 "-[UIImage imageNamed:]" 发生了什么?

左图 Aseet/ 右图 Folder
上图 Aseet/ 下图 Folder

使用 Asset 底层是使用一个叫 _UIAssetManager 的类去存取图片,而使用 Folder 则是走 imageIO。而且即使你是用 Folder 也会先判断 Asset 中没有这张图片才去走 imageIO 。

这里就不展开说,具体可以根据图中的函数名下符号断点跟踪。(建议使用 chisel 来看各个类的成员变量)

缓存结构和索引的不同

使用 Asset 的缓存结构是: CUIStructuredThemeStore(https://github.com/billinghamj/iPhone6-iOS8-RuntimeHeaders/blob/master/PrivateFrameworks/CoreUI.framework/CUIStructuredThemeStore.h)

// _cache
[
Hash(A.png) -> 缓存
Hash(B.png) -> 缓存
]

使用 Folder 的缓存结构是: CUIMutableStructuredThemeStore(https://github.com/JaviSoto/iOS10-Runtime-Headers/blob/master/PrivateFrameworks/CoreUI.framework/CUIMutableStructuredThemeStore.h)

// nameIdentifierStore
[
A.png -> 1
B.png -> 2
]

// memoryStore
[
Hash_(1) -> 缓存
Hash_(2) -> 缓存
]

因为使用 Folder 去查找缓存,会先遍历 nameIdentifierStore 查找是否有缓存,没有就从 Folder 读取。在 Time Profiler 会有那么多的 "isEqualTo" 比较。

而使用 Asset 则是直接根据图片名称来直接 "objectForKey:" 取出缓存。

两者速度比较是: 前者 O(n)+O(1),后者 O(1)。
(至于为什么是O(n)? 因为我观察有O(n)这个行为,且 "isEqualTo" 的参数刚好字典 keys 和当前需要图片的名字。)

而且看出来建立这个缓存的时候,使用 Asset 会快一点(只需要一个字典就可以)。

文件 IO 的不同

假设都是读取 5 张图片

使用 Asset 的 IO 过程是:
open Asset -> read Asset -> close Asset

使用 Folder 的过程是:
open 1.png -> read 1.png -> close 1.png
open 2.png -> read 2.png -> close 2.png
open 3.png -> read 3.png -> close 3.png
open 4.png -> read 4.png -> close 4.png
open 5.png -> read 5.png -> close 5.png

可以看出这里 IO 次数少了(如果数量大的话,比较更加明显,Asset 一直是 1 次IO,而 Folder 会随读取文件的多少而递增),而且在 IO Usage 可以看出读取 Asset 的速度极快。

asset.car 到底是什么

asset.car 是编译后打包到项目里的文件。从目前的研究来看,asset.car 其实是将资源打包并建立索引的二进制文件,其头部包含资源在二进制对应的位置(类似 seekTable)。

小结

所以使用 Asset 的速度快的原因有以下几点:

  1. 缓存格式较优,从而建立和查找缓存的速度也会加快。
  2. 图片储存方式较优,查找图片位置更快,IO 也更快。

从 Folder 到 Asset,你需要做的只是转移图片资源(Xcode支持将Folder图片一键导入),并且不需要改任何代码就能使图片加载速度大大加快。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,993评论 19 139
  • 在第13章“高效绘图”中,我们研究了和Core Graphics绘图相关的性能问题,以及如何修复。和绘图性能相关紧...
    雪_晟阅读 658评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,598评论 25 708
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,147评论 5 13
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,554评论 0 17