在 iOS 上查找每个外部库贡献的大小

Find size contributed by each external library on iOS

我正在尝试减小我的应用商店二进制大小,我们有很多外部库可能会增加最终 ipa 的大小。有没有办法找出每个外部静态库在最终二进制文件中占用了多少(除了要删除每个库?)?

我认为你应该能够从中提取你需要的信息:

symbols -w -noSources YourFileHere

参考:https://devforums.apple.com/message/926442#926442

IIRC,它不会给你每个库的清晰摘要信息,但你应该发现每个库的函数应该聚集在一起,所以稍微努力一下你可以计算出来自每个库:

还要确保在构建设置中将“生成调试符号”设置为“否”。这可以将静态库的大小减少大约 30%。

如果这是您关注的一部分,静态库就是将相关的 .o 文件归档在一起加上一些簿记。因此,一个 1.7mb 的静态库——即使其中的代码是整个 1.7mb——通常不会给你的产品增加 1.7mb。有关死代码剥离的通常规则将适用。

除此之外,您还可以减少代码的构建大小。以下可能不是一个完整的列表。

在目标的构建设置中查找 'Optimization Level'。通过将其切换为 'Fastest, Smallest -Os',您将允许编译器为大小牺牲一些速度。

确保您正在为 thumb 构建更紧凑的 ARM 代码。假设您使用的是 LLVM,这意味着确保您在项目设置中的任何地方都没有 -mno-thumb。

还要考虑您要为哪些架构构建。 Apple 不允许提交同时支持 ARMv6 和 iPhone 5 屏幕的应用程序,并且已从最新的 Xcode 中完全放弃对 ARMv6 的支持。所以现在可能没有必要包括它。

所有这些信息都包含在 link 地图中,如果您有耐心筛选它(对于大型应用程序,它可能会很大)。 link 地图列出了所有库、它们的目标文件以及打包到您的应用程序中的所有符号,所有这些都是人类可读的文本。通常,项目未配置为默认生成它们,因此您必须快速更改项目文件。

来自 Xcode:

  1. 在您的目标 'Build Settings' 下,搜索“地图”
  2. 在下面的结果中,在 'Linking' 部分下,将 'Write Link Map File' 设置为“是”
  3. 确保记下 'Path to Link Map File'
  4. 下列出的完整路径和文件名

下次您构建应用程序时,您会得到一个转储到该文件路径的 link 地图。请注意,该路径是相对于您的应用程序在 DerivedData 文件夹中的位置(通常是 ~/Library/Developer/Xcode/DerivedData/<your-app-name>-<random-string-of-letters-and-numbers>/Build/Intermediates/...,但 YMMV)。由于它只是一个文本文件,您可以使用任何文本编辑器阅读它。

link 地图的内容分为 3 个部分,其中 2 个部分与您要查找的内容相关:

  1. 目标文件:此部分包含最终应用程序中包含的所有目标文件的列表,包括您自己的代码和您包含的任何第三方库的代码。重要的是,每个目标文件还列出了它来自的库;
  2. 部分:此部分与您的问题无关,包含处理器段及其部分的列表;
  3. 符号:这部分包含您感兴趣的原始数据:所有symbols/methods的列表及其绝对位置(即处理器内存映射中的地址)、大小和最重要的,对其包含的对象模块的交叉引用(在 'File' 列下)。

根据这些原始数据,您拥有进行所需大小计算所需的一切。从#1 可以看出,对于每个库,都有 N 个可能的组成对象模块;从#2 中,您可以看到,对于每个目标模块,有 M 个可能的符号,每个符号占用大小 S。对于任何给定的库,您的大小粗略顺序将类似于 O(N * M * S)。这只是为了让您了解将进入实际计算的组件,它不是任何有用的公式。要自行执行计算,我很遗憾地说,我不知道有任何现有工具可以为您完成必要的处理,但考虑到 link 地图只是一个文本文件,带有一点脚本魔法和独创性,您可以构建一个脚本来完成繁重的工作。

例如,我有一个 link 到以下库的小示例项目:https://github.com/ColinEberhardt/LinqToObjectiveC (the sample project itself is from a nice tutorial on ReactiveCocoa, here: http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1),我想知道它占用了多少 space。我生成了一个 link 地图,TwitterInstant-LinkMap-normal-x86_64.txt(它在模拟器中运行)。为了找到库包含的所有目标模块,我这样做:

$ grep -i "libLinqToObjectiveC.a" TwitterInstant-LinkMap-normal-x86_64.txt

这给了我这个:

[  8] /Users/XXX/Library/Developer/Xcode/DerivedData/TwitterInstant-ecppmzhbawtxkwctokwryodvgkur/Build/Products/Debug-iphonesimulator/libLinqToObjectiveC.a(LinqToObjectiveC-dummy.o)
[  9] /Users/XXX/Library/Developer/Xcode/DerivedData/TwitterInstant-ecppmzhbawtxkwctokwryodvgkur/Build/Products/Debug-iphonesimulator/libLinqToObjectiveC.a(NSArray+LinqExtensions.o)
[ 10] /Users/XXX/Library/Developer/Xcode/DerivedData/TwitterInstant-ecppmzhbawtxkwctokwryodvgkur/Build/Products/Debug-iphonesimulator/libLinqToObjectiveC.a(NSDictionary+LinqExtensions.o)

第一列包含我需要的符号 table 的交叉引用,因此我可以搜索这些:

$ cat TwitterInstant-LinkMap-normal-x86_64.txt | grep -e "\[  8\]"

这给了我:

0x100087161 0x0000001B  [  8] literal string: PodsDummy_LinqToObjectiveC
0x1000920B8 0x00000008  [  8] anon
0x100093658 0x00000048  [  8] l_OBJC_METACLASS_RO_$_PodsDummy_LinqToObjectiveC
0x1000936A0 0x00000048  [  8] l_OBJC_CLASS_RO_$_PodsDummy_LinqToObjectiveC
0x10009F0A8 0x00000028  [  8] _OBJC_METACLASS_$_PodsDummy_LinqToObjectiveC
0x10009F0D0 0x00000028  [  8] _OBJC_CLASS_$_PodsDummy_LinqToObjectiveC

第二列包含相关符号的大小(十六进制),所以如果我将它们全部加起来,我得到 0x103,即 259 个字节。

更好的是,我可以进行一些流式处理,将其精简为基本元素并为我添加:

$ cat TwitterInstant-LinkMap-normal-x86_64.txt | grep -e "\[  8\]" | grep -e "0x" | awk '{print }' | xargs printf "%d\n" | paste -sd+ - | bc

直接给我数字:

259

"\[ 9\]"(13016 字节)和 "\[ 10\]"(5503 字节)执行相同的操作,并将它们添加到前面的 259 个字节,得到 18778 个字节。

你当然可以改进我在这里做的流黑客攻击,让它更健壮一点(在这个实现中,你必须确保你得到正确的 spaces 的确切数量并引用括号),但你至少明白了。

为您的应用制作一个 .ipa 文件并将其保存在您的系统中。

然后打开终端执行以下命令:

unzip -lv /path/to/your/app.ipa

它将 return 一个 table 关于您的 .ipa 文件的数据。大小列包含 .ipa 文件中每个文件的压缩大小。