iOS 性能优化:优化 App 的持久化策略
1 使用 HEIC 格式图片
2 将图片放入 AssetCatalog
3 选择合适的持久化策略
持久化存储原理介绍
文档数据元数据 File System Metadata
Metadata 的数据写入经常会发生,而且 IO 开销是很大的,例如你的 App 中有一个 plist 文件记录上一次的启动时间,每次启动 App,都读取该 plist 获取上次启动时间,然后写入当前时间这个简单的操作会发生一次读取 IO,三次写入 IO,还有一次 fsync() 的操作,并且以下行为都会造成 File System Metadata 写入。
- 创建文件
- 删除文件
- 重命名文件
- 更新文件
而 File System Metadata 包括以下元素
- 文件名
- 大小
- 地理位置
- 修改时间
等等....
syncing to disk
缓存结构OS cache:性能最好的一层,使用 logical I/O,由于是储存在内存中,所以 I/O 操作很高效(使用 logical I/O)
Disk cache:磁盘储存的物理映射。(使用 physical I/O)
permanent storage:最终用于持久化数据的介质,对于 iOS 来说,就是闪存(使用 physical I/O)
缓存有以上几个层级,对于 App 来说,离 cpu 越近的 cache,性能就越好,但同时我们也希望 cache 能确实地落在磁盘中。数据在内存当中时对于 App 而言速度是最快的,也没有任何的 IO 开销,但是当我们需要将数据从内存一层一层地注入到闪存时,就需要注意 IO 开销了.
fsync()
该函数用于将数据从 OS cache 层写入到 Disk cache 层,但数据可能不是立即写到 permanent storage 层。
FULLSYNC
该函数用于将数据从 OS cache 层写入到 permanent cache 层,并且会触发所有已经存在于 disk cache 上的数据写入到 permanen cache。
文件序列化格式的选择
开发者一般会使用 Plist,XML,JSON 这三个常见的格式,这些都是常见的数据格式,便于使用而且普适性高,也易于解析,适合不是频繁读写的数据,但是每次改动都是全量的读写,导致整个文件读取和重新写。
sqlite
关于直接使用 sqlite ,苹果特别提出了关闭与开启连接的开销,每次开启和关闭DB的连接,都会触发 sqlite 的一致性检测,日志恢复,日志标志位设置等等操作,因此 apple 建议不要过多的开启和关闭连接,而是在 app 的生命周期里,开发者尽可能的保持连接一直开启,例如可以建立一个独立的子线程来保持与DB的连接,然后全局通过那个子线程去操作 DB。
日志
关于日志,开发者平时可能对于 sqlite 日志的 mode 没有过多的关注,但其实日志 mode 的不同对性能同样有很大的影响,Delete Mode
是 sqlite 的默认日志 mode,但WAL Mode
是更推荐的日志 mode,首先是因为更少的写操作,这个日志模式会自动组合多个写操作到同一页,同时也使用更少的 barrier,支持多个读操作与写操作并发,并且支持数据快照,例如我们要写入 4 页的 DB,WAL Mode并不会分别写在这 4 个页中,而且统一写在 Write Ahead log file 中。
事务 Transaction
在多个 INSERT,UPDATE,DELETE 操作时,建议使用 Transaction,可有效减低 IO 次数。
FileSizeandPrivacy
当我们从 DB 中删除数据时,在 DB 中储存该数据的空间会被设置为可用,但被删除的数据是实际上有可能仍然在磁盘上直至有新数据写入,如果是涉及安全和敏感的数据,可以使用PRAGMA schema.auto_vacuume=INCREMENTAL
这种删除模式,该模式会总动清理被删除的数据,并且在 iOS13 是默认模式。
不要使用 VACUUM
VACUUM 是性能比较差的清扫空闲页的方式,建议使用PRAGMA schema.auto_vacuume=INCREMENTAL。
作者:RyRYanZhong,iOS 开发,字节跳动研发工程师
链接:iOS 性能优化:优化 App 的持久化策略
来源:老司机技术周报公众号
著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。