【问题标题】:Find size contributed by each external library on iOS查找 iOS 上每个外部库贡献的大小
【发布时间】:2015-11-07 07:24:04
【问题描述】:

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

【问题讨论】:

    标签: ios xcode linker uikit


    【解决方案1】:

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

    在 Xcode 中:

    1. 在目标的“构建设置”下,搜索“地图”
    2. 在下面的结果中,在“链接”部分下,将“写入链接映射文件”设置为“是”
    3. 确保记下“链接映射文件的路径”下列出的完整路径和文件名

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

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

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

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

    例如,我有一个小示例项目,它链接到以下库:https://github.com/ColinEberhardt/LinqToObjectiveC(示例项目本身来自关于 ReactiveCocoa 的一个很好的教程,这里:http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1),我想知道有多少空间它占据。我生成了一个链接图,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)
    

    第一列包含对我需要的符号表的交叉引用,所以我可以搜索那些:

    $ 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 $2}' | xargs printf "%d\n" | paste -sd+ - | bc
    

    这给了我直接的数字:

    259
    

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

    您当然可以改进我在此处所做的流黑客攻击,使其更加健壮(在此实现中,您必须确保获得正确的空格数并引用括号),但您在至少明白这个想法。

    【讨论】:

    • 感谢您的精彩回答。我会尝试写一些东西来计算贡献的大小。
    • 很抱歉挖掘了这个话题,但这个SO 也可以作为精确计数的替代方案。
    • @Ethenyl,只有在您拥有相关第三方库的实际源代码时才有效,但通常情况并非如此。
    • @fullofsquirrels 是的,正如您所指出的,第 3 方代码总是很难从中获取指标。也取决于包管理器。
    • @SamhanSalahuddin 你有没有写过脚本来计算贡献的大小?我正在寻找这样做(对于内部和外部库),但如果它已经完成,那么很高兴看到一个要点或其他东西!
    【解决方案2】:

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

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

    解压 -lv /path/to/your/app.ipa

    它将返回有关您的 .ipa 文件的数据表。 size 列包含 .ipa 文件中每个文件的压缩大小。

    【讨论】:

    • 问题是关于静态链接到二进制文件的静态库大小。您的回复仅列出了 .ipa 中的文件,但二进制文件仅为 1 个文件。
    【解决方案3】:

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

    符号 -w -noSources YourFileHere

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

    IIRC,它不会为您提供有关每个库的清晰摘要信息,但您应该发现每个库中的函数应该聚集在一起,因此您可以通过一些努力来计算每个库的近似贡献:

    【讨论】:

    • 外部链接在关闭一段时间后似乎又可以正常工作了(尽管您确实需要登录)。可能是 Apple 论坛的临时问题。
    【解决方案4】:

    还要确保在构建设置中将 Generate Debug Symbols 设置为 NO。这可以将静态库的大小减少大约 30%。

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

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

    在目标的构建设置中查找“优化级别”。通过将其切换为“Fastest, Smallest -Os”,您将允许编译器牺牲一些速度来换取大小。

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

    还要考虑您要构建的架构。 Apple 不允许提交同时支持 ARMv6 和 iPhone 5 屏幕的应用程序,并且从最新的 Xcode 中完全放弃了对 ARMv6 的支持。所以在这一点上可能没有意义。

    【讨论】:

    • 我们决定使用 swift。增加 5 - 8 MB。 :(
    猜你喜欢
    • 1970-01-01
    • 2018-10-26
    • 2015-02-13
    • 2010-11-04
    • 2014-10-16
    • 2012-07-29
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    相关资源
    最近更新 更多