ILRuntime热更案例学习(五) ------ 注意事项总结

iOS IL2CPP 打包与热更新技巧
本文详细介绍了在iOS环境下使用IL2CPP打包Unity项目时的注意事项,包括如何通过配置link.xml文件避免类型裁剪,处理泛型实例和泛型方法的问题,以及在热更新开发中避免跨域调用的建议。同时,提供了Android和iOS平台热更新方案的选择指导。

官方示例下载地址1: https://github.com/Ourpalm/ILRuntime

官方示例下载地址2 : https://github.com/Ourpalm/ILRuntimeU3D

官方文档地址 : https://ourpalm.github.io/ILRuntime/public/v1/guide/tutorial.html


一.iOS IL2CPP打包注意事项

1.类型裁剪

IL2CPP在打包时会自动对Unity工程的DLL进行裁剪,将代码中没有引用到的类型裁剪掉,以达到减小发布后ipa包的尺寸的目的。然而在实际使用过程中,很多类型有可能会被意外剪裁掉,造成运行时抛出找不到某个类型的异常。特别是通过反射等方式在编译时无法得知的函数调用,在运行时都很有可能遇到问题。

在Unity工程的Assets目录中建立一个叫link.xml的XML文件,然后按照下面的格式指定你需要保留的类型:

<linker>
  <assembly fullname="UnityEngine" preserve="all"/>
  <assembly fullname="Assembly-CSharp">
    <namespace fullname="MyGame.Utils" preserve="all"/>
    <type fullname="MyGame.SomeClass" preserve="all"/>
  </assembly>  
</linker>

2.泛型实例

每个泛型实例实际上都是一个独立的类型,List<A> 和 List<B>是两个完全没有关系的类型,这意味着,如果在运行时无法通过JIT来创建新类型的话,代码中没有直接使用过的泛型实例都会在运行时出现问题。

在ILRuntime中解决这个问题有两种方式,一个是使用CLR绑定,把用到的泛型实例都进行CLR绑定。另外一个方式是在Unity主工程中,建立一个类,然后在里面定义用到的那些泛型实例的public变量。这两种方式都可以告诉IL2CPP保留这个类型的代码供运行中使用。

因此建议大家在实际开发中,尽量使用热更DLL内部的类作为泛型参数,因为DLL内部的类型都是ILTypeInstance,只需处理一个就行了。此外如果泛型模版类就是在DLL里定义的的话,那就完全不需要进行任何处理。

3.泛型方法

跟泛型实例一样,foo.Bar<TypeA> 和foo.Bar<TypeB>是两个完全不同的方法,需要在主工程中显式调用过,IL2CPP才能够完整保留,因此需要尽量避免在热更DLL中调用Unity主工程的泛型方法。如果在iOS上实际运行遇到报错,可以尝试在Unity的主工程中随便写一个static的方法,然后对这个泛型方法调用一下即可,这个方法无需被调用,只是用来告诉IL2CPP我们需要这个方法

 二.开发注意事项

热更部分的代码都不继承MonoBehaviour,也就是都不挂脚本,非热更部分随意:热更对MonoBehaviour这种比较特殊东西的支持都挺麻烦,要么不用,要么只是做个不可热更的消息转发层;要么开发时挂脚本,打包时用某种特殊的方式把它变成代码里动态AddComponent。

尽量不要跨域调用啥的

三.热更方式选择

1. Android
不用任何第三方的热更方案,用C#反射执行DLL,性能和代码写法和纯C#基本一样。
Google Pay强制要求在2019年8月之前App都支持64位,Unity的应对方案是Android IL2cpp,暂时没有支持mono backend 64位的打算。所以到时候只能是IL2CPP + ILRuntime的方式,性能会差一大截,主要慢在ILRuntime上。

不过现在年底了,博主打算不考虑iOS和Android的区分了~~~

2. iOS
ILRuntime + DLL 解释执行,当然是在IL2CPP下。

 

在 Unity3D 开发中,ILRuntime 是一个常用的 .NET 新解决方案,它允许在不重新发布应用的情况下新游戏逻辑。然而,由于 ILRuntime 的运行时机制与标准 .NET 环境存在差异,某些依赖于反射或特定运行时特性的库可能无法直接在 ILRuntime 中正常工作。 关于 `protobuf-net` 在 ILRuntime 中的支持情况,以下是一些关键点: - `protobuf-net` 是一个基于 .NET 的高效二进制序列化库,广泛用于网络通信和数据持久化。它依赖于 .NET 的反射机制来动态生成代码以提高序列化性能[^1]。 -ILRuntime 环境下,由于其对反射的支持有限,`protobuf-net` 的某些功能可能无法正常运行。特别是那些依赖于动态类型生成和即时编译的功能[^2]。 - 为了在 ILRuntime 中使用 `protobuf-net`,通常需要进行一些适配和修改。例如,可以通过预生成序列化代码的方式来绕过运行时反射的需求。这种方式需要在构建阶段为所有需要序列化的类型生成对应的 `ProtoBuf.Serializer` 实现,从而避免在运行时进行动态代码生成[^3]。 - 另一种替代方案是使用 `protobuf-net` 的 AOT(Ahead-of-Time)编译版本,该版本可以在 ILRuntime 中运行。AOT 编译版本通过在构建时生成所有必要的序列化代码,避免了对反射的依赖,从而提高了兼容性[^4]。 ### 示例:使用 AOT 编译版本的 `protobuf-net` 假设你有一个简单的数据类 `PlayerData`,你可以通过以下步骤在 ILRuntime 中使用 `protobuf-net`: 1. **定义数据类** ```csharp [ProtoContract] public class PlayerData { [ProtoMember(1)] public string Name { get; set; } [ProtoMember(2)] public int Level { get; set; } } ``` 2. **生成 AOT 编译代码** 使用 `protogen` 工具生成 AOT 编译版本的 `PlayerData` 类: ```bash protogen -i:PlayerData.proto -o:PlayerData.cs ``` 3. **在 ILRuntime 中使用** 确保生成的代码被正确导入到 Unity 项目中,并在 ILRuntime 环境中调用 `Serializer.Serialize` 和 `Serializer.Deserialize` 方法: ```csharp // 序列化 using (var stream = new MemoryStream()) { Serializer.Serialize(stream, playerData); byte[] data = stream.ToArray(); } // 反序列化 using (var stream = new MemoryStream(data)) { PlayerData loadedData = Serializer.Deserialize<PlayerData>(stream); } ``` ### 注意事项 - 在使用 `protobuf-net` 的 AOT 编译版本时,确保所有需要序列化的类型都在构建阶段生成了对应的序列化代码- 如果遇到性能问题或功能限制,可以考虑使用其他轻量级的序列化库,如 `MessagePack-CSharp` 或 `FlatBuffers`,它们在 ILRuntime 中的支持可能为完善。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值