分析对象:
x.ipa(X / 原 Twitter iOS 客户端)分析方式:对解压后的
Payload/Twitter.app做目录体积、Mach-O 段/节(segment/section)和加密状态分析日期:2026-06-19
x.ipa 大的根本原因不是图片/资源,而是「代码量本身」——一个超大型「超级 App」把海量一方代码 + Kotlin 跨端共享层 + 一大堆身份核验/支付/风控/实时音视频第三方 SDK 全部静态打进了同一个包。
| 维度 | 数值 |
|---|---|
x.ipa(压缩后) |
408 MB |
Twitter.app(解压后) |
510 MB |
其中 Frameworks/ |
367 MB(占 72%) |
其中 PlugIns/(扩展) |
71 MB(14%) |
其中 Assets.car(图片资源) |
23 MB(4.5%) |
| 仅前 4 个 framework | ≈ 280 MB(占整包 55%) |
仅前 4 个 framework 的纯机器码(__text) |
≈ 166 MB |
一句话:体积 ≈ 巨量编译后代码(__text),而且整包还处于 App Store FairPlay 加密状态(cryptid=1),加密段几乎不可压缩,所以 510 MB 压成 ipa 也只能压到 408 MB(仅缩 20%)。
| 项 | 值 |
|---|---|
| 应用 | X(原 Twitter) |
| Bundle ID | com.atebits.Tweetie2 |
| 版本 | 12.1(build 11) |
| 最低系统 | iOS 15.0 |
| CPU 架构 | 仅 arm64(已做 App Store thinning,不是肥二进制) |
| 主可执行文件 | Twitter(3.2 MB) |
说明:
Chirp不是二进制,而是字体目录(X 自有 Chirp 字族 + 一个 3.6 MB 的 emoji 覆盖可变字体)。
| 组件 | 体积 | 占比 | 说明 |
|---|---|---|---|
Frameworks/ |
367 MB | 72% | 绝对大头,57 个动态库 |
PlugIns/ |
71 MB | 14% | 4 个 App 扩展 |
Assets.car |
23 MB | 4.5% | 图标/图片资源编译产物 |
Localization_*.bundle |
9.3 MB | 本地化资源 | |
Chirp/(字体) |
5.3 MB | 自有字体 + emoji 字体 | |
Grok_Grok.bundle |
4.6 MB | Grok(AI)资源 | |
XIcons / TwitterAppearance 等 bundle |
各 ~4 MB | 图标/主题资源 | |
| 其余几十个资源 bundle + lproj | 余下 |
| Framework | 体积 | 性质 |
|---|---|---|
| TwitterAppSPMMigration | 87 MB | 一方 Swift 主体代码(模块化迁移中) |
| TwitterSPMMigration | 81 MB | 一方 Swift 主体代码(模块化迁移中) |
| XKotlin | 78 MB | Kotlin 跨端共享业务逻辑(Kotlin/Native 编译) |
| T1Twitter | 42 MB | 遗留 ObjC/Swift 核心("T1"=Tweetie 血统) |
| XCoreLibraries | 16 MB | 一方基础库 |
| WebRTC | 11 MB | 实时音视频 |
| LiveKitWebRTC | 11 MB | 实时音视频(与上面重复) |
| Persona2 | 8.6 MB | 身份核验 / KYC |
| SocureDocV | 7.7 MB | 证件文档核验 |
| PalmAPISaas | 5.9 MB | 掌纹 / 生物识别 |
| LinkKit (Plaid) | 4.8 MB | 银行账户连接 |
| PinwheelSDK | 4.4 MB | 收入 / 银行数据 |
| VeryAILiveness | 3.0 MB | 活体检测 |
| MarkdownView / SwiftProtobuf / MobileIntelligence / CastleSDK … | 1–2 MB 一档 | 长尾 |
前 4 个 framework 合计 ≈ 280 MB,占整包的 55%。
对最大的几个二进制做 Mach-O 段/节剖析,结果非常明确:大头是 __text(编译后的机器指令),不是数据、不是资源。
TwitterAppSPMMigration(87 MB)段内构成:
| Section | 大小 | 含义 |
|---|---|---|
__text |
53.3 MB | 纯机器码 ← 主体 |
__eh_frame |
3.0 MB | C++/异常展开表 |
__objc_methname |
1.6 MB | ObjC 方法名字符串 |
__swift5_typeref |
1.1 MB | Swift 类型元数据 |
__cstring |
1.1 MB | C 字符串 |
__objc_methlist |
1.1 MB | ObjC 方法列表 |
| 其余 Swift/ObjC 元数据 | 分散数 MB | 反射/字段/协议元数据 |
TwitterSPMMigration 同型:__TEXT 段就有 61 MB。
两个名字里带 "SPMMigration" 的巨型 framework 是关键信号:X 正处于从单体工程向 Swift Package Manager 模块化迁移的中途,绝大部分一方业务代码被塞进了这两个「迁移过渡壳」里(一个 App 层、一个非 App 层)。它们没有被拆细,所以各自高达 80+ MB。这部分是「业务复杂度 + 迁移中间态」共同导致的,纯代码体积,无法靠压缩资源解决。
XKotlin.framework(78 MB)是用 Kotlin Multiplatform 写的、给 iOS/Android 复用的共享业务逻辑,经 Kotlin/Native 编译成原生 framework:
| Section | 大小 | 含义 |
|---|---|---|
__text |
41.2 MB | Kotlin 编译出的机器码 |
__LINKEDIT |
23.7 MB | 符号表 / 导出符号(Kotlin/Native 导出极多符号) |
__DATA_CONST.__const |
8.6 MB | Kotlin/Native 运行时常量数据 |
Kotlin/Native 的产物天生偏大:它要自带运行时和 GC,并生成大量符号与反射元数据(这就是 23.7 MB __LINKEDIT 的由来)。一个共享层就吃掉 ~78 MB。
__text 18.8 MB,且 ObjC 元数据特别多(__objc_methname 1.56 MB、__objc_const 1.44 MB)。这是 Tweetie 时代延续下来的老 Twitter for iOS 核心代码,和上面的 SPMMigration 新代码并存——新旧两套同时在包里,是迁移期的典型现象。
X 要做「万能 App / X Money」,因此集成了大量重型 SDK,集中在三类:
- 身份核验 / KYC / 活体:Persona2(8.6M) + SocureDocV(7.7M) + PalmAPISaas(5.9M) + VeryAILiveness(3M) + AmazonFaceLiveness + XPalmLiveness + XFaceLiveness ≈ 30+ MB
- 支付 / 银行:LinkKit·Plaid(4.8M) + PinwheelSDK(4.4M) + Payments ≈ 10+ MB
- 风控 / 反欺诈:CastleSDK + MobileIntelligence + DeviceRisk + IOSSecuritySuite
这些功能多数用户从不触达,但代码全部静态打进主包。
-
两套 WebRTC:
WebRTC.framework(11.0 MB)和LiveKitWebRTC.framework(10.8 MB)几乎是同一套实时音视频栈的两份独立构建,≈ 21 MB 重复能力。 -
扩展重复嵌入资源 bundle:
ShareExtension.appex(42 MB)把主 App 已有的资源 bundle 又打了一份:资源 bundle 主 App ShareExtension TwitterAppearance 4.0 MB 4.0 MB XIcons 3.9 MB 3.9 MB DMV2Assets 2.3 MB 2.3 MB Periscope 系列 4.2 MB 4.2 MB TwitterUIResources 0.7 MB 0.7 MB 仅此一项 ShareExtension 就重复了约 15 MB。(其本体二进制还另有 22 MB。)
-
7 个
*_PackageProduct.framework:SPM 产物残留的薄壳 framework,属于打包链路的边角冗余。
检查加密状态发现 cryptid = 1——不仅主二进制,连 framework 也是 FairPlay 加密的:
主 Twitter 二进制 : LC_ENCRYPTION_INFO_64 cryptid 1 cryptsize 2,392,064
TwitterAppSPMMigration : LC_ENCRYPTION_INFO_64 cryptid 1 cryptsize 70,238,208 (≈70MB)
这说明该 ipa 是未脱壳的 App Store 原始包。加密后的代码段是高熵数据,几乎不可被 zip 压缩。普通 App 通常能压掉 50%+,而这里 510 MB 只压到 408 MB(仅 20%),正是因为大量 __text 处于加密态。换句话说:代码多 + 代码被加密,双重导致 ipa 文件体积下不来。
| 常见怀疑 | 实测 | 结论 |
|---|---|---|
| 肥二进制(多架构) | 仅 arm64 | ✅ 已 thinning,无问题 |
| 本地化语言太多 | 43 种语言,仅 172 KB | ✅ 可忽略 |
| 图片资源爆炸 | Assets.car 23 MB |
✅ 对这个体量的 App 算克制 |
| 字体 | 5.3 MB(含 3.6 MB emoji 字体) | 一般 |
资源不是元凶,代码才是。
- 拆分 SPMMigration 巨壳 + 完成模块化迁移:把 TwitterAppSPMMigration/TwitterSPMMigration(合计 168 MB)真正拆成可按需/可裁剪的模块,是体积优化的最大盘子。
- 统一 WebRTC:LiveKit 与裸 WebRTC 二选一或共用底层,省 ~10–20 MB。
- 第三方 SDK 按需化:身份核验/支付/活体类 SDK 改为 On-Demand Resources / App Clips / 动态下发,绝大多数用户无需在首装时下载这 40+ MB。
- 消除扩展资源重复:ShareExtension 通过 App Group / 共享 framework 复用主 App 资源,省 ~15 MB。
- XKotlin 瘦身:开启 Kotlin/Native 的死代码消除、
-Xg0/release 符号裁剪,压缩 23.7 MB 的__LINKEDIT。 - 清理 7 个 PackageProduct 残壳。
注意:第 1、5 项(一方代码与 Kotlin 层)是体积主体但最难动;第 2、3、4、6 项是「低风险、可立即回收」的约 50–60 MB。
# 架构确认(全部 arm64)
lipo -archs Twitter / *.framework → arm64
# 段构成(最大二进制)
size -m TwitterAppSPMMigration → __text 53.3MB / __TEXT 段 70MB
size -m XKotlin → __text 41.2MB / __LINKEDIT 23.7MB / __const 8.6MB
size -m T1Twitter → __text 18.8MB(ObjC 元数据密集)
# 加密状态
otool -l ... | grep LC_ENCRYPTION_INFO → cryptid 1(主二进制与 framework 均加密)
# 体积
du -sh Frameworks = 367M;前 4 framework ≈ 280M;整包 510M;ipa 408M