UH4L21——重定向的书写规则
UH4L21——重定向的书写规则
本章代码关键字
1234567ILIntepreter.AppDomain //通过解释器获取appDomainILIntepreter.Minus() //移动栈指针,获取不同位置,可用于获取栈底或者指向某个参数的地址Type.CheckCLRTypes() //将StackObject.ToObject()转换出来的对象再为某种类型的对象(返回出来的值可直接强转成对应的类型)StackObject.ToObject() //将某个栈空间存储的值转换为对象CLR.Utils.Extensions.TypeFlags //要转换的类型枚举,配合Type.CheckCLRTypes()使用ILIntepreter.Free //释放某个栈上的内存ILIntepreter.PushObject() //将复杂类型的返回值压入到栈内
重定向的书写规则
当我们需要自己实 ...
UH4L20——解释器
UH4L20——解释器
ILRuntime中的解释器
当ILRuntime通过 Mono.Cecil 库将DLL文件中的 IL 中间语言读取出来后,
会利用已经写好的相关代码,将IL代码解释翻译,之后用于执行
它主要做了(ILIntepreter脚本):
遍历方法体中的每一条指令
使用内部巨大的 switch case 用于处理 IL 中的每一条指令(判断不同的指令类型来处理对应逻辑)
在执行代码时,使用ILRuntime内部自己实现的运行栈来管理内存,ILRuntime中使用非托管内存,内存不会被GC管理,
而是ILRuntime内部自己管理,通过指针直接对内存进行操作(脚本RuntimeStack),其中使用自定义类 StackObject 来表达基础类型值
ILRuntime中的内存布局
在ILRuntime实现的非托管 运行栈 中存储的对象主要就是 StackObject 对象
在该运行栈中,StackObject 对象是依次排列的,我们只需要移动当前的栈指针(加一或者减一)就可以获取到栈中存储的各数据
123456789101112 ...
UH4L19——类型系统
UH4L19——类型系统
类型系统的作用
类型系统用于表示热更工程中的各种类型、方法、成员、实例对象等等信息
相当于,ILRuntime帮助我们利用它内部的解释器,将我们实现的代码解释翻译为了自己的一套类型规则
在真正执行代码时,都是基于ILRuntime自己的类型系统中的规则来执行的
说人话:类型系统 是 热更工程中的类、方法、成员等信息 的载体
如何理解这个所谓载体
假设我们在ILRuntime的热更新工程内创建了一个Test类,里面有成员属性A和成员方法TestFun
这个类随热更新工程会被编译为DLL文件,在DLL文件内部就包含了编译出来的Test类的IL中间代码,
我们并不能直接在主工程内调用这个DLL文件内部的类,因此需要使用ILRuntime来帮助我们获取、记录并执行其中的IL中间代码
而ILRuntime会借助Mono.Cecil读取DLL文件内部的IL中间代码,获取其中的类型和元消息,这里面就包括Test类和它的成员
ILRuntime在读取到类型消息和元消息后,就会翻译这些信息,然后使用类型系统下继承IType的类对象记录的翻译出来的消息
而Test ...
UH4L18——基本原理
UH4L18——基本原理
ILRuntime基本原理
ILRuntime借助Mono.Cecil库来读取DLL中的IL汇编码,然后通过内置的IL解释执行虚拟机来执行DLL中的代码
C#的运行机制
该知识点在 Unity进阶之C#知识补充 中详细讲解过,详见:CS5L1——.NET相关知识
C# 通过C#编译器 生成 对应的 CIL(通用中间语言)
然后通过 CLR(公共语言运行时)将 CIL 解释翻译 为最终的机器码,运行在平台上
那么我们生成DLL文件,其中存储的就是我们的 CIL 中间语言,我们可以通过 反编译工具 ILSpy 来查看对应的 IL 中间代码
注:上图不是ILSpy而是dySpy,就查看IL中间代码的能力是一致的,dySpy已停更
ILSpy获取地址:icsharpcode/ILSpy (github.com)
IL的汇编代码中有很多的指令信息,如果想要学习他们,可以前往微软官网查看具体的IL汇编指令的作用
OpCodes 类 (System.Reflection.Emit) | Microsoft Learn
ILRuntime实现热更新的基础——Mon ...
UH4L17——性能优化相关
UH4L17——性能优化相关
本章代码关键字
1appDomain.Prewarm() //提前预热加载某个类或者方法,提高第一次执行时的性能
ILRuntime性能测试或打包时的注意事项(设置相关)
如果要进行ILRuntime性能测试,或最终打包,为了达到最好的性能表现,需要注意以下几点
热更工程生成的DLL包使用Release编译生成
在Unity编辑器中的执行效率会低于真机,内存占用也会相差数倍,建议真机测试性能和内存
打包时不要勾选 Development Build(开发构建),若勾选打包后真机测试效率会较低
如果是IL2CPP打包,在 PlayerSetting 中 的 Other Setting 中需要将 C++ Compiler Configuration(C++编译器配置)设置为 Release,
否则效率较低(Unity2018以后)
ILRuntime性能测试或打包时的注意事项(代码相关)
如果要进行ILRuntime性能测试,或最终打包,为了达到最好的性能表现,需要注意一下几点
一定要进行CLR绑定, ...
UH4L16——寄存器模式
UH4L16——寄存器模式
本章代码关键字
1234567ILRuntimeJITFlags //ILRuntime的即时编译(JIT)模式ILRuntimeJITFlags.None //不启用寄存器模式ILRuntimeJITFlags.JITOnDemand //按需即时编译(JIT)模式,全局开启时,一般只会使用该模式ILRuntimeJITFlags.JITImmediately //立即JIT模式ILRuntimeJITFlags.NoJIT //禁用JIT模式ILRuntimeJITFlags.ForceInline //强制内联模式,该模式只对方法的Attribute生效[ILRuntimeJIT()] //对类和方法使用,指定为某个类或者方法开启寄存器模式
寄存器模式
寄存器模式是 ILRuntime2.0 版引入的专用于优化大规模数值计算的执行模式
该模式通过 ILRuntime 自己的 编译器以及 ...
UH4L15——调试相关
UH4L15——调试相关
本章代码关键字
12appDomain.DebugService.StartDebugService() //注册调试服务appDomain.DebugService.IsDebuggerAttached //判断调试器链接
ILRuntime调试相关
注:笔者实际测试下来出现了无法命中断点的问题,原因不明且无法解决
进行ILRuntime调试需要安装插件
想要进行断点调试,我们需要注册调试服务,想要及时进断点,那么可以配合协同程序等待调试器链接
可以利用IP和端口调试特点来调试移动设备
准备ILRuntime调试相关插件
ILRuntime 2.1.0以下的版本 需要前往ILRuntime的Github页面获取插件
地址:Releases · Ourpalm/ILRuntime (github.com)
打开页面后,点击右侧的Releases,在对应版本处下载调试插件
ILRuntime 2.1.0及其以上的版本
在VS上方的:拓展-管理拓展-搜索ILRuntime后安装
当VS的菜单栏的调试处多了一个Attach ...
UH4L14——序列化库
UH4L14——序列化库
本章代码关键字
1JsonMapper.RegisterILRuntimeCLRRedirection() //ILRuntime修改的LitJson特有的方法,热更工程如果要使用则必须调用该方法注册,传入appDomain
ILRuntime中使用序列化库
序列化库在我们开发当中经常会用到,比如之前我们学过的 LitJson、Protobuf
但是这些库都是存在于主工程中的,那么当使用他们序列化反序列化热更工程中的对象时,他们是不能识别的
所以在ILRuntime中使用序列化库,需要对其进行修改
LitJson库获取:Demo工程中就有修改好的LitJson
Protobuf库获取:protobuf-net: Protobuf-net for ILRuntime (gitee.com)
注意:改写序列化库时,不能通过Activator来创建实例
ILRuntime在使用第三方库时,为了能够正常的对热更工程中声明的类对象进行使用
我们往往需要对其进行修改,修改第三方库对于大家来说可能有一定难度
所以首先去ILRuntime的群和社区中去找找有没有 ...
UH4L13——Unity反射调用ILRuntime
UH4L13——Unity反射调用ILRuntime
ILRuntime反射相关
Unity中反射使用ILRuntime热更工程中内容
通过IType获取类型消息
12IType type = appdomain.LoadedTypes["TypeName"];Type t = type.ReflectedType;
不能使用Activator.CreateInstance 或 new T() 创建实例
只能通过创建ILRuntime中对象中的反射方式创建
appdomain.Instantiate 或者 type.GetConstructor 后 Invoke
ILRuntime热更工程中使用反射,和C#中使用反射一样
在热更工程中使用反射
按照反射的规则正常调用即可,和C#中反射没有任何区别
在Unity工程中调用反射热更工程内容
假设要反射调用下面的热更新工程内的类
123456789101112131415161718192021222324252627282930313233343536373839404142434 ...
UH4L12——协同程序和异步函数
UH4L12——协同程序和异步函数
协同程序和异步函数
之所以需要注册跨域继承适配器,是因为在ILRuntime中的协同程序和异步函数
编译后本质上是通过状态机利用对象的状态来达到的异步,这里面的对象就用到了跨域继承
所以我们需要注册他们的跨域继承适配器来让热更新工程正常使用他们
在ILRuntime热更工程中使用协同程序
如果不注册就直接在热更工程内开启协程,会报错
12345678910111213141516171819public class ILRuntimeMain{ public static void Main() { Lesson16 lesson16 = Camera.main.GetComponent<Lesson16>(); lesson16.StartCoroutine(Lesson16Test()); } public static IEnumerator Lesson16Test() { Debug.Log(0); yi ...