今日头条优化实践: iOS 包大小二进制优化,一行代码减少 60 MB 下载大小

苹果对iOS App大小有下载和可执行文件大小限制,影响用户下载和App上架。今日头条探索实践__TEXT段迁移技术,将可执行文件的__TEXT段部分节移到其他段,避开加密机制,提高压缩效率,减小下载大小32%,解决了相关限制问题,并分享实践中问题的解决办法。

摘要

苹果对 iOS App 大小有严格限制:下载大小超限会阻碍用户在蜂窝网络下载 App ,直接影响新用户转化;可执行文件超限将导致 App 审核被拒,直接影响上架。今日头条探索实践 __TEXT 段迁移技术,成功减小下载大小 32%,并且解决了可执行文件大小受限问题。

一、背景知识

1. 下载大小限制

App 大小有下载大小和安装大小的概念。

下载大小是指 App 压缩包(也就是 .ipa 文件)所占的空间,用户在下载 App 时,下载的是压缩包,这样做可以节省流量;当压缩包下载完成后,就会自动解压,解压过程也就是通常所说的安装过程;安装大小就是指压缩包解压后所占用的空间。

安装大小在 App Store 上就可以看见 ,通常它会影响用户的下载意愿:

而下载大小只有研发人员在 App Store Connect 后台才可以看,用户看不见,它影响的是下载消耗的流量和时长:

下载大小超过限制,将无法使用蜂窝网络下载 App( iOS 13 之前),会收到文件容量太大的提示,需通过 Wi-Fi 网络下载。如下,为苹果历年来对 App 下载大小限制的变化情况:

  • 2008 年 7 月,搭载了 App Store 的 iPhone 3G 正式发售,下载限制仅为 10 MB

  • 2010 年 2 月,苹果将 iPhone 3G 的下载限制从 10 MB 提升到 20 MB

  • 2012 年 3 月,iOS 5.1 正式版后,下载限制从 20 MB 提升到 50 MB

  • 2013 年 9 月,iOS 7 正式版后,下载限制从 50 MB 提升至 100 MB

  • 2017 年 9 月,iOS 11 正式版后,下载限制从 100 MB 提升至 150 MB

  • 2019 年 5 月,下载限制从 150 MB 提升至 200 MB

  • 2019 年 9 月,iOS 13 正式版后,若下载大小超过 200 MB,用户可选择是否使用蜂窝网络下载

如今,App 下载大小超出 200 MB 时 ,会出现两种情况:

  • iOS 13 以下的用户,无法通过蜂窝数据下载 App

  • iOS 13 及以上的用户,需要手动设置才可以使用蜂窝网络下载 App

2. 可执行文件大小限制

根据最大构建版本文件大小[1]描述,苹果对可执行文件大小亦有明确限制,超过该限制会导致 App 审核被拒:

ERROR: ERROR ITMS-90122: "Invalid ExecutaBe Size. The size of your app's executaBe file 'News.app/News' is 68534272 bytes for architecture 'arm64', which exceeds the maximum allowed size of 60 MB."

具体限制如下:

  • iOS 7 之前,二进制文件中所有的 __TEXT 段总和不得超过 80 MB

  • iOS 7.X 至 iOS 8.X ,二进制文件中,每个特定架构中的 __TEXT 段不得超过 60 MB

  • iOS 9.0 之后,二进制文件中所有的 __TEXT 段总和不得超过 500 MB

二、面临问题

随着网络普及、流量费用降低,苹果已经放宽了限制。但下载大小若超出 200 MB,可以肯定对新增仍会有一定影响。这对上亿级用户的 App 来说是巨大的损失,并且本着追求极致、在竞品中拔得头筹的理念,我们认为下载大小 200 MB 是包大小的一根红线。

今日头条 App 的下载大小已经接近 180 MB,而经过了多年的极致优化(包括但不限于代码/图片/其它资源的优化、编译/链接参数的优化、推进无用业务下线、准入卡口等),已经很难再有较大幅度的减少。为此平台和各方都投入了极大的人力、甚至牺牲了业务的迭代空间来优化/抑制下载大小。

2020 年下半年,我们另辟蹊径探索实践了 __TEXT 段迁移的方法:将可执行文件的 __TEXT 段中的部分节移动到其它的段,避开苹果的加密机制,提高了可执行文件的压缩效率,使 App 的下载大小减少了 60 MB。

该方案彻底解决了下载大小限制的问题,同时还解决了仍在支持 iOS 8.X 的 App 面临的可执行文件大小限制问题。

三、技术原理

1. Mach-O 文件格式简介

iOS 可执行文件是 Mach-O 格式,主要由 HeaderLoad CommandsData 三部分。

可以简单认为:

  • Header 描述了文件的大概信息。

  • Load Commands 由多条 Load Command 组成,它们描述了 Data 在二进制文件和虚拟内存中的布局信息,有了这个布局信息就能够知道 Data 在二进制文件中和虚拟内存中是怎样排布的,它相当于修房子时的图纸一样。

  • Data 存储了实际的内容,主要是程序的指令和数据,它们的排布完全依照 Load Commands 的描述。

Mach-O 文件中的 Data 部分主要是以 Segment(中文翻译为段)和 Section (中文翻译为节)的方式来组织内容的,好比学校中有年级和班级、公司中有部门和小组一样,把有共同特点的内容组织到一块,可以方便管理,提高效率。

使用 $ xcrun size -lm <binary-path> 指令可以查看 Mach-O 文件 Data 部分的结构和各 Segment/Section 的大小信息(该 Mach-O 文件由 Xcode 的 iOS App 模板工程构建而来)。在不需要更详细的信息时,这条命令很方便。

上图就展示了 Data 中的内容排布的基本信息。

由该图可知,在该文件中:

  • Data 部分中有 5 个 Segment,依次是:

    • __PAGEZERO

    • __TEXT

    • __DATA_CONST

    • __DATA

    • __LINKEDIT

  • __PAGEZERO__LINKEDIT外,每个段中有多个 Section

注意:Data__DATA 是不同的两个概念。Data 是 Mach-O 文件中的一部分,包含多个段。__DATA 只是 Data 中的一个段。

__PAGEZERO 的大小是 4 GB,但并不是它在 Mach-O 文件中的真实大小。这 4 GB 是 Mach-O 加载进内存后, __PAGEZERO 在内存中占中的大小,它不可读,不可写,主要用来捕捉 NULL 指针的引用。如果访问 __PAGEZERO 段,会引起 EXC_BAD_ACCESS 错误。__PAGEZERO 在 Mach-O 中实际上并不占用 Data 部分的空间。

__TEXT__DATA_CONST__DATA 用于保存程序的代码指令和数据。

__LINKEDIT 包含启动 App 需要的信息,比如 bind & rebase 的地址,代码签名,符号表等。

2. __TEXT 段迁移的原理

程序的构建过程包含 预处理 -> 编译 -> 汇编 -> 链接 等 4 个主要阶段,完成之后就会得到 Mach-O 可执行文件。

通过 $ man ld ,可以发现链接器有一个参数: -rename_p orgSegment orgSection newSegment newSection。使用该参数可以将orgSegment/orgSection的名称修改为newSegment/newSection

可以在 Other Linker Flags 中传递该参数。如:

-Wl,-rename_p,__TEXT,__text,__BD_TEXT,__text
-Wl,-segprot,__BD_TEXT,rx,rx

其中 -Wl 的作用是告诉 Xcode 它后面的参数是添加给 Ld 链接器的,这些参数将在链接阶段生效。

第一行参数会新创建一个 __BD_TEXT 段,并把 __TEXT,__text 移动到 __BD_TEXT,__text

第二行参数是给 __BD_TEXT 赋予可读和可执行权限。

构建完成后再来看一下移动 __TEXT,__text 后的 Mach-O 文件:

可以看到 __TEXT,__text 已经被移动到了 __BD_TEXT 中去了,它的地址也由起始的 0x100005e5c 变为了 0x100010000 。此时程序仍可以正常的运行,这是因为操作系统只关心段的读/写/执行权限,并不关心段或节的名称。即便是使用了 -rename_p 移动 Segment/Section,各符号的地址也会由链接器修正好,因此段移动后程序也可以正常运行。

在最低支持 iOS 8 的时代,很多大型 App 都遇到过可执行文件中 __TEXT 段超 60 MB 的问题。facebook[2] 当时采用了 -rename_p 的技术来避免该问题。他们使用的链接参数为:

-Wl,-rename_p,__TEXT,__cstring,__RODATA,__cstring
-Wl,-rename_p,__TEXT,__gcc_except_tab,__RODATA,__gcc_except_tab
-Wl,-rename_p,__TEXT,__const,__RODATA,__const
-Wl,-rename_p,__TEXT,__objc_methname,__RODATA,__objc_methname
-Wl,-rename_p,__TEXT,__objc_classname,__RODATA,__objc_classname
-Wl,-rename_p,__TEXT,__objc_methtype,__RODATA,__objc_methtype

参数的作用是将 __TEXT 中的 __cstring__gcc_except_tab__const__objc_methname__objc_classname__objc_methtype<

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值