[转载] 存储空间终于算"对"了

转载: 原文地址

为什么 iOS 主流 App 显示的总大小都无法对齐系统”iPhone存储空间”内的显示?本文分析了差异的主要原因和新版快手存储空间页如何做到对齐系统,通过深入研究苹果文件系统的机制和一系列实验验证,抽丝剥茧一步步确认了iOS存储空间的显示口径。

总结

  • 原因一:App自身大小

  • 原因二:进制差异

  • 原因三:口径差异

  • 原因四:统计路径差异

背景介绍

我们收到用户反馈,快手App内显示的已用存储空间与iOS系统设置中显示的大小不一致。在调研了几款主流App后发现各家显示的大小和系统或多或少都有些差异,App的存储空间的占用作为近些年微博热搜的常客,这个问题引起了我们的极大的兴趣和关注。

iOS 获取App磁盘占用大小的常规方式就是通过官方NSFileManager配合NSDirectoryEnumerator遍历所有文件来计算整体沙盒大小。但是这种方式计算的总大小始终无法和系统设置中的总大小对齐。

潜在影响

App内显示空间占用和系统不一致很容易引起用户对我们App真实占用大小的疑问,而且多数情况下,系统显示的总大小会明显大于我们App内显示数据。数据的不一致性可能会导致用户对App的信任度下降。用户可能会怀疑App的质量和准确性,甚至担心隐私和数据安全问题。

差异探寻

原因一:App自身大小

通过直接对比App内显示已用空间和系统总大小,首先可以找到一个显著差异:在系统中,”iPhone存储空间”点击每个App会看到”App大小”和”文稿与数据”,除了QQ以外,其他App都没有将自身大小计入已用空间。

对于iOS开发者来说,一个App的总大小由App大小和沙盒大小组成这一概念可能就像条件反射一样觉得理所当然。但是对于大多数普通用户而言,就无法理解系统”iPhone存储空间”列表中显示的总大小由哪些细分组成,应该用总大小还是某个细分大小对应App中显示的大小。 

所以需要从普通用户的视角,匹配最直观的认知方式,对齐系统”iPhone存储空间”显示的总大小和App内整体占用大小,将App大小也计入App整体占用大小。

原因二:进制差异

现象:为什么1TB的硬盘实际显示只有931GB?

descript

当购买一块新的移动硬盘时,会选择不同的容量大小,512GB、1TB、2TB等等,但是当我们打开电脑链接上时可能会发现,可用的容量比产品规格中的标注稍稍小了一点。这主要是由于存储设备的制造商使用十进制(1000)来测量存储容量,而一些系统显示大小是基于二进制(1024)计算的。

假设:苹果用的是10进制,类似硬盘规格

通过查阅苹果官方文档[How storage capacity is measured on Apple devices]我们发现,苹果在标注设备的存储容量时会使用10进制为标准,那么为了配合这一口径,系统中关于存储容量的显示应该也需要对齐这个口径以减少用户的困扰。

同时参考SI(International System of Units)、BIPM(International Bureau
of Weights and Measures)等相关国际组织的定义[Binary prefix],KB、MB和GB等单位按照标准,应使用10进制。

descript

二进制和十进制的差异,在MB和GB级别分别会导致4.9%和7.4%的误差[Binary prefix],所以我们需要确认iOS系统显示所使用的口径是否为十进制。

descript

验证

可以通过对比”iPhone存储空间”页中的”App大小”和实际.app 大小来确认。

通过SSH登陆到越狱机后,使用Cycript注入指定进程,执行 [[NSBundle mainBundle] bundlePath]获取对应App的.app目录后,再使用 du 命令返回.app总大小。

descript

du -s 查看当前目录总磁盘占用空间

descript

1
2
3
4
359652KiB = 359652 / 1024 = 351.22265625 MiB ≈ 351.22MiB

359652KiB = 359652 * 1024 / 1000 000 = 368.283648 MB ≈ 368.3MB

du命令默认使用二进制进行单位转换[Linux manual page],分别根据二进制和十进制转换后,可以确认iOS系统存储单位为十进制,且保留有效小数机制为四舍五入。

无论是iOS还是macOS在用户UI界面显示中都采用了10进制的存储空间单位换算,客观上就从普通用户感知层面避免了了类似Windows系统中硬件规格说明和系统显示的差异问题造成的困扰。

原因三:口径差异

现象:为什么1B(字节)的文件磁盘大小是4KB?
Finder(访达)看这个文件的信息描述。
descript

假设:文件实际大小和磁盘占用大小是两个概念

可以看出,4KB以下小文件对结果的影响最大,而我们App沙盒中恰恰有存在大量这些小文件(缩略图、小数据包、临时缓存等),所以两个口径对我们整体大小的计算可能会造成非常大的差异。

1个1字节的文件 1万个1字节的文件 1万个10 KB的文件
文件实际大小 1 B 10 000 B = 10KB 100 000 000 B = 100 MB
磁盘占用大小 4 KB 40 000 000 B = 40 MB 120 000 000 B = 120 MB
差异 75% 99.975% 16.67%

文件实际大小:是指文件自身的实际大小,1B(字节)的文件”文件实际大小”即为1B(字节) 

磁盘占用大小:指文件已占用的磁盘空间(即使没有全部被使用,剩余空间不能存放其他文件的数据,”宁缺毋滥”),1B的文件在macOS/iOS系统中””磁盘占用大小”为4KB。查阅资料发现实际磁盘是按块为单位进行存储的,由于文件的大小 并不一定是块的整数倍,因此最后一个块可能仅被部分占用,而该块剩余的空间将保持未使用的状态(其他文件也不能使用)。

验证

我们可以通过直接往App沙盒中增加 10000 个 1000B(实际文件大小)的文件到沙盒,然后查看最终在”iPhone存储空间”页增量来判断。

如果最终增量为10 MB则采用了文件实际大小计算,如果增量为40MB (10000 * 4KB)则采用了文件磁盘占用大小计算。

SSH 登录到 iPhone 后 直接在对应沙盒目录中写入文件

1
2
for i in {1..10000}; do dd if=/dev/zero of=file$i.txt bs=1000 count=1;
done

最终增量为0.04GB=40MB,说明iPhone在计算存储空间时使用了”磁盘占用大小“来计算总的存储空间占用。

App内计算磁盘占用大小

那么我们如何在App内计算文件的”磁盘占用大小”呢?通过NSFileManager直接获取的是文件的”文件实际大小”,而计算文件的磁盘占用大小就需要通过磁盘块。

通过标准头文件stat.h的st_blocks可以获取到块的数量,通过 st_blocks * 512字节 可以计算出文件的磁盘占用大小。

之所以块(扇区)大小是512字节参考[Lecture 2: Introduction to Filesystems],这种做法源自较早的磁盘扇区大小标准。这种设计是基于早期磁盘技术的物理限制,尽管现代硬盘的物理扇区大小可能已经增加到了 4096 字节,但 512 字节的逻辑块大小在许多文件系统和操作系统的接口中仍然被保留。参考扇区是磁盘读写的最小物理单位节

当进行磁盘操作时,无论是读取还是写入,系统必须至少处理一个完整的扇区。这是因为磁盘的设计不允许单独修改扇区中的一部分数据,而是必须读取整个扇区,进行修改后再整体写回。

descript

实际操作中,我们发现在iOS中文件的st_blocks总是8的倍数(即使文件实际大小只有1字节),说明iOS的文件系统(APFS)层级的最小的块大小定义为4096字节,而APFS的官方文档中也佐证了这一观点。

参考文档 Apple-File-System-Reference.pdf
descript

原因四:统计路径差异 Library/Caches/

我们尝试使用官方的 NSURLVolumeAvailableCapacityForImportantUsageKey 属性获取iOS系统磁盘剩余空间时发现,系统并没有把Library/Caches/ 计入已占用空间,在手机剩余容量吃紧的时候,Library/Caches/ 目录下的内容会被系统清理。苹果文档 File System Programming Guide 也提到了存储在不同目录下的文件有着不同的生命周期,其 Library/Caches/ 应该存储app运行非必须的数据。

验证

基于这一现象,我们针对沙盒的几大主要目录,通过写入已知大小的冗余文件,对系统显示大小和App内显示大小进行对比,确认了iOS系统纳入大小统计的沙盒目录。

iOS 17 的新问题

在本地测试我们发现 iOS17 以上系统,部分App的总大小在”iPhone存储空间”页的显示逻辑和iOS16
及以下的不同。在iOS17中,部分App只含有”文稿与数据”一个细分项,没有计入”App大小”。而根据实测发现,”文稿与数据”中计算的是整个沙盒的大小,和iOS16及以下机型”文稿与数据”中剔除了Library/Caches大小也有所区别。

关于iOS 17 App总大小显示的口径差异目前没有定位到任何官方文档说明,且只出现在部分App上,如果大家发现相关说明/解释 或一些不同的展示方式,欢迎联系我们一起讨论沟通

结论

iPhone计算App总占用空间时的计算口径如下:

总结

快手对齐后效果

在快手新版本的App中,我们不仅加入了App自身大小的统计,将存储空间单位的进制统一切换到了10进制,还采用文件磁盘空间占用的方式来计算总大小,并对齐iOS系统计算存储空间的路径范围,以便与iOS系统的显示方式保持一致。目前,快手App内显示的已用空间已能与iOS系统显示基本对齐。

技术视角和用户视角的思考

在App存储空间的研究和优化中,用户体验始终是我们关注的核心。数据的准确性固然重要,但更加重要的是从用户的视角出发,关注用户在使用过程中的认知成本和直观感受。突出整体交互的直观性、易用性以及信息的透明度。

处理App存储空间的问题时,我们不仅解决了技术上的挑战,还特别重视用户界面的直观性和信息透明度。通过对齐系统大小、划分清理模块,我们帮助用户更加轻松地管理App存储空间,最终增强用户对快手的信任和满意度。

参考资料

  1. How storage capacity is measured on Apple devices
  2. Binary prefix
  3. Linux manual page
  4. opensource.apple.com/source/xnu
  5. Lecture 2: Introduction to Filesystems
  6. 磁盘扇区 Disk sector
  7. File System Basics
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道