iOS 禁 JIT,是 Unity 热更这条技术线上一切方案的起点:程序集 AOT 编译后无法运行时生成新代码,所以”发版后改逻辑”必须绕道。这篇沿时间线把四代方案——tolua、xlua、ILRuntime、HybridCLR——各解决什么、各留下什么坑讲清楚。看完你应该能回答一个问题:我的项目,2026 年该选什么,以及什么时候根本不需要热更。

地基:AssetBundle 与双通道约束
先立地基。AssetBundle 是编辑器期构建、运行期加载的资源包,能装模型、材质、场景,唯独不能装脚本——这条限制决定了 Unity 热更永远是两条通道:资源热更(AB 及其继任者)+ 代码热更(另一想办法)。
AB 侧有一组至今没变的工程事实:包内资源有依赖,共享资源必须显式归包,否则重复打包且丢失合批;加载是三阶段(AB 内存镜像 → Load 反序列化出 Asset → Instantiate 复制),所以卸载要按引用计数走,Unload(true) 时机不对就是满屏紫材;粒度是权衡——太碎管理开销大,太大带着无用资源跑。
手写 AB 管理的更新流程,2016 年那批项目的形态大致是:服务器按「版本号/平台」放资源 + files.txt(文件名 + MD5 + 大小),客户端带时间戳拉清单防 HTTP 缓存,逐文件比 MD5 差异下载;StreamingAssets 只读,首启复制到 persistentDataPath 才能增量更新,「两次解包」(先 Lua 与主界面、再全量带进度条)压首启等待。
# files.txt 的极简形态(2016 手写流)
lua/login.lua md5:a3f2... size:4096
lua/battle.lua md5:7b1c... size:81921
ab/ui.unity3d md5:dd90... size:1048576清单机制 + 差异下载 + 只读目录解包——这套东西的工程形态,你今天在 Addressables 的 catalog 和 YooAsset 的清单里仍能看到影子。还有一条血泪铁律:热更界面与更新逻辑本身必须极简不变——更新流程出 bug 是无法用”热更修复”来救的,它自己就是那个通道。
第一代:tolua + LuaFramework
既然 AB 热不了 C#,第一代答案是引入 Lua 虚拟机:C# 写壳,易变逻辑走 Lua,两边靠 tolua# 的 Wrap 文件桥接。LuaFramework(PureMVC + tolua#)是那个时代的标准骨架——Lua 侧 MVC 三层,三个环境开关(LuaBundleMode / UpdateMode / DebugMode)区分编辑器开发与出包。
这套方案能用,但代价从一开始就写好了。双语言意味着团队要同时精通两套生态、两套调试链、两套性能模型——Lua 侧还得懂 LuaJIT 那些事(local 缓存、避免 NYI 操作、控制存活临时变量),Wrap 文件让 C# API 变更有传染性。当时业内公认的经验法则是”UI 与易变模块走 Lua,战斗等性能敏感模块留 C#“,这个划分本身就是对方案局限的承认。另外 LuaJIT 导出 bytecode 时要记得带调试信息参数,否则线上出错只能盲猜——这类小坑只有真维护过的人才知道。
那为什么它当年能成为事实标准?因为约束条件下它是正确答案:iOS 审核周期以周计、Lua 虚拟机是现成的、社区有 LuaFramework 这种开箱即用的骨架。评价技术选型永远要看当时的约束,而不是拿今天的答案嘲笑昨天的题。
什么时候不该用(现在回看):团队没有 Lua 底子、项目生命周期两年以内的中小项目。双语言的固定成本要在多年、多次热更里摊销,摊不回来就是纯亏损。
第二代:xlua 与 ILRuntime 的两条中间路线
第二代没有收敛成一个方案,而是分出两条路线,恰好对应两类痛点。
xlua(腾讯)是 Lua 路线的集大成:比 tolua 先进的不是性能,是工程化——更完善的桥接生成、更活跃的社区,以及 canary 级的用法如「Lua 热补丁给 C# 打补丁」。它解决的是第一代 Lua 方案的工程粗糙问题,但双语言割裂这个根本代价一点没少。市面上大量项目至今停在 xlua,不是因为它是终点,而是因为迁移有成本而它”还能用”。
ILRuntime 是另一条路线的先声:不引入 Lua,直接解释执行 C# 的 IL,用反射桥接调用主工程。它承诺了”单语言热更”,但落地的人都知道坑:解释执行性能差、跨域调用有大量坑、调试要靠专门插件,泛型和值类型的支持常年是补丁摞补丁——值类型跨域传参、ref/out、泛型实例化,每一项都有一段”文档说支持、真机炸给你看”的往事。同一时期还有更早的探索——基于 IL 注入的 C#ToIL 补丁方案、CShotfix、ET 框架——说明”摆脱 Lua”的需求真实存在,只是还没有一个工程上足够稳的答案。
这两代方案留下最重要的遗产是共识:热更链路的状态机化(Launch → CheckVersion → PreLoad → GameInit,断线重连回归 GameInit 重建数据)、统一加载接口、先编辑器模拟再真机验证——这些架构原则跟具体方案解耦,换热更框架时应该原样带走。
当前:HybridCLR 与资源侧的 Addressables / YooAsset
2021 年后 HybridCLR 基本终结了路线之争。它的思路釜底抽薪:既然 AOT 平台禁的是”运行时生成新代码”,那就让热更程序集走解释执行,AOT 程序集照常原生运行——同一个项目里 C# 代码一部分 AOT、一部分热更,无脚本层割裂,无 Wrap 生成,跨语言调用开销消失,调试、泛型、现有类库生态全都是原生 C# 体验。
代价与前提要说清楚:解释执行比原生慢(性能敏感的战斗核心逻辑仍可留 AOT 侧,或等 HybridCLR 的商业化进阶版本);热更程序集与 AOT 程序集的引用方向要规划(AOT 可引用热更的接口,热更可引用 AOT 的实现,反过来不行);AOT 泛型缺元数据要靠 supplemental metadata files 补,漏补就是运行时炸泛型,这是接入时最常被问的坑;接入成本虽低但热更分包策略、版本管理这些工程层的事它不管——HybridCLR 解决的是”代码能热”,不是”热更体系”。
资源侧同步换代:Addressables(官方,自动依赖解析与增量构建)与 YooAsset(国产商用级)取代了手写 AB 管理,当年 AssetBundle Manager 里的 Simulation Mode、本地资源服务器这些概念被完整继承——验证了这条线的演进是渐进改良而非革命。
怎么选:2026 年的决策清单
新项目的决策其实很短:
- 默认答案:HybridCLR + Addressables / YooAsset。除非有压倒性理由,不要在 2026 年新开 Lua 双语言项目。
- 存量 xlua 项目:不亏待它,也别加码。在它稳定服役期间把热更链路(清单、校验、分包、状态机)和具体脚本方案解耦,等有窗口期再迁 HybridCLR——迁移成本主要在工程层,恰好是你已经解耦好的那部分。
- 先问要不要热更:三个月一发、渠道可控、用户强更容忍度高的项目,热更体系的维护成本可能大于收益。热更是逃生通道,不是架构信仰——为不存在的危机预付复杂度,是新手团队最常犯的过度设计。
- iOS 审核政策是变量:热更的合规边界历史上反复摇摆,方案选型时要留”合规收紧也能活”的退路,这也是把热更范围收窄(只更逻辑与修复,不更大模块)的另一个理由。
十五年看下来,这条线的演进方向出奇一致:离 Lua 越来越远,离原生 C# 越来越近,双通道(资源 + 代码)架构从头到尾没变过。每一代方案都是在”iOS 禁 JIT”这个固定约束下重新回答同一个问题,答案越来越接近”假装没有约束”。理解了这两句,就能在下一代方案出现时自己判断它是不是真进步——判据很简单:它有没有减少语言割裂,有没有降低工程复杂度,还是只是换了一层新的割裂。