- 背景
- iOS无法热更的原因
- 苹果AppStore的审核准则中,明令禁止应用程序分配具有可执行权限的内存
- 带有JIT功能的脚本虚拟机无法运行
- 无法加载动态链接库
- 安卓Google Play强制64位之后也必须使用IL2CPP了
- Lua 和il runtime 都可以解释执行
- 从2015年开始研发,现在最新版本是2.0
- 线上有多款mmo,卡牌,休闲,棋牌游戏使用il runtime
- iOS无法热更的原因
2、原理
C#的运行原理:c#代码编译成为cil(中间语言),然后再由cil 编译成clr(公共语言),最后编译成对应的机器码
MSIL 和MonoCecil 是两个关键的类库
实现原理:
读取dll中类型和方法的元信息,还有方法体中的il汇编指令,和dll对应的pdb调试符号文件
将要修改的信息读取出来之后,进行修改,然后再写回到dll
======================================
3、概念注意点:
- 对于il runtime 来说,dll 就是普通的二进制资源文件,所以可以用assetbundle的方式压缩或者加密,(同时对主工程也可以采用unity的方案进行加固)
- Il runtime 中创建的对象为非托管内存,需要自己管理内存,可以通过指针进行内存操作
- Il runtime的解译器就是一个巨大的switch case 方法,将c#编译出来的中间汇编语言进行执行
- Il runtime 有两种模式,一个是栈式vm,另一个是寄存器式vm
- 汇编指令越多,则解释器性能越低
- 栈式vm:优点是跨平台,缺点是指令数多
- 寄存器vm:优点是指令数少(因为和硬件关联),缺点是对应特定的平台架构(如x86)
- 不支持 unsafe ,volatile,p/Invoke等需要和硬件或者操作系统打交道的功能
4、工程结构
由不需要热更的主工程和热更部分的热更工程,两个工程。
每次出包之前,需要将热更工程导出为dll,放在主工程streamAsset目录下
5、il runtime 的实现细节
- iType:所有il runtime 内类型的基类
- iMethod:所有方法的基类
- ILTypeInstance:所有的实例均为该类型
6、il runtime 的使用
- CLR 重定向
- 委托的使用
- 携程和异步的使用
- 反射接口的使用
- 序列化库
注意点:跨域继承不能多继承,否则会找不到对应的适配器
7、性能如何
- Il runtime vs lua (各项数据(如计算,原生接口调用)均好于lua)
- 配置表的读取应该按需读取,否则会占用很大内存
- Il runtime 2.0中添加了寄存器模式,可以在进行密集计算时提高性能
- 由于il runtime 有懒加载机制,所以需要使用预热机制来解决第一次比较慢的问题
- 热更工程调用主工程,性能要高于主工程调用热更工程,所以要合理规划项目结构(一些框架性的底层东西可以放在主工程,一些业务逻辑或者变化频繁的内容则放在热更工程)
- Update的逻辑也应该放在主工程中,通过事件的方式对热更工程调用
- 消耗较大的独立算法,如物理计算或者寻路算法,等工具类的方法应该在主工程中
- Foreach 中 使用值类型,在il runtime 下会有频繁的装箱拆箱操作,会导致gc的产生
- 注册值类型绑定会减少gc 回收的调用
- 正确使用clr绑定会提高执行速度
- 需要在release模式下测试(editor下会有很多辅助工具,会影响测试结果,或者debug模式下也会有很多调试信息的处理)
8、热更范围的划分
主工程:
资源管理,场景管理,网络消息管理,ui框架,战斗框架,公共的底层组件
协议,序列化和解析
Sdk集成
第三方插件
热更工程:
战斗的业务逻辑
Ui业务逻辑
协议,配置表
Sdk调用逻辑
注意点:
跨模块调用应使用事件派发来降低耦合,方便后续热更
使用link.xml预留主工程接口
需要主工程处理的事件中使用object的万能参数
使用xil 的aop热修复方案提供主工程代码的保底
不要在热更工程频繁使用monobehaver,因为会设计到预制和对应脚本上的字段的序列化的问题,这部分在unity引擎层比较复杂并且序列化接口也没有开放