Unity进阶--C#补充知识点--【Unity跨平台的原理】Mono与IL2CPP

·来源于唐老狮的视频教学,仅作记录和感悟记录,方便日后复习或者查找


一.跨平台基本原理

知识回顾:

  • ①在之前我们已经知道了跨语言的原理是.Net体系下定义了这些语言需要遵守的工业标准CLI。因此实现了面向.Net的语言都可以被编译转化成统一规范的CIL公共中间语言。
  • ②而要实现下一步跨平台就需要我们把CIL再转化为对应平台的可执行机器码。

而对于第二步如何进行对CIL进行处理就有两种方式:Mono或者IL2CPP


二.Mono

关于Mono是什么不记得了可以回去看之前的笔记:Unity进阶--C#补充知识点--【Unity跨平台的原理】了解.Net

1.Unity与Mono的关系

Mono:

  • 诞生于2004年
  • 能够实现跨语言和跨平台两方面的功能

Unity:

  • 公司诞生于2004
  • Unity的底层是由C++实现的,但是希望上层有更方便易学的语言来提供编程
  • 同时希望制作的工程游戏能够在多个平台上运行

综合Unity的需求和Mono的功能,我们可以很容易地看出Unity当时为什么会选用Mono:因为它同时满足了Unity需要跨语言和跨平台的需求

2.Unity跨平台的基本概念

Unity的两个主要部分:

Unity Engine(引擎部分):提供UnityEngine.dll动态库,各平台不同,由C/C++编写,包含平台相关代码,图形API,物理引擎,灯光等等所有游戏引擎的底层部分

Unity Editor(编辑器部分):提供UnityEditor.dll动态库大部分由C#编写,用户的脚本最开始还可以用C#,JavaScrpit,Boo语言编写(不过最后因为C#太好用了成为主要语言),项目代码最后由Mono进行编译

Mono的组成:

①C#编译器(msc)

②Mono Runtime Mono运行时:类似于CLR公共语言运行时(虚拟机)

包含JIT(just in time)即时编译器,AOT(Ahead of time)提前编译器,GC,类库加载器等等

③BCL基础类库

④Mono类库

它提供了很多.Net之外的额外功能,主要用于构建操作系统上的应用

3.用Mono实现Unity跨平台的基本原理

Mono实现Unity跨平台的原理示意图

①先把在Unity开发中使用的各种语言编译为CIL公共中间语言

②之后在对应平台上通过Mono VM(Mono虚拟机)把CIL转化为平台对应的机器码

4.用Mono实现Unity跨平台的优缺点

优点:

①理论上可以在无数个平台上实现无数个虚拟机

缺点:

①维护工作费时费力,每次版本更新的时候都要更新所有的Mono虚拟机

②低版本的Mono无法支持新版本C#的新特性

注意:Mono是JIT,即边编译边运行的。

Unity利用Mono实现上层平台的跨语言和跨平台,这是由Unity和Mono的组成结构决定的功能


三.IL2CPP

1.IL2CPP是什么

IL2CPP是继Mono之后Unity提供的又一个跨语言跨平台的解决方案。

我们可以在项目设置中把脚本后处理方法设置为IL2CPP

注意这个需要把该版本的Unity中安装对应平台的IL2CPP模块才可以使用他来打包工程

2.IL2CPP的工作原理

①IL2CPP顾名思义就是把IL中间语言转化为C++代码,

②因为各个平台基本上都有支持C++的优化过的编译器,所以可以再把C++编译为对应平台上对应的机器码。

③值得注意的是虽然变成了C++代码,但是内存管理还是采用C#的方式,因此这里仍然需要来一个IL2CPP VM虚拟机来进行内存的分配与管理以及线程创建等工作

3.IL2CPP和Mono的区别

Mono:

  • ①打包速度快
  • ②Mono是用JIT即时编译,支持更多类库
  • ③必须把代码发布为托管程序集
  • ④MonoVM虚拟机平台维护麻烦,且部分平台不支持(WebGL)
  • ⑤由于Mono版本受限,因此C#的很多新特性无法使用
  • ⑥IOS仅支持64位的Mono应用提交到应用商店

IL2CPP:

  • ①打包速度略慢
  • ②用AOT提前编译完成后才能运行(这使得运行速度更快)
  • ③可以启用代码剥离功能减少工程大小
  • ④多平台移植更方便

最大的区别就是:

  • Mono是JIT边运行边编译,因此可以运行的时候动态生成代码和类型
  • IL2CPP是AOT提前编译后运行,无法动态生成代码与类型,因此需要在编译的时候就完全确定好需要的类型(不过对于一些定义了的类型,但是没有具体显式调用过,但是在热更新的时候调用到了,那就会发生报错,具体的解决方案写在后面)

4.IL2CPP的优缺点

优点:

①运行速度快,效率高

②各个平台上方便维护

③因为有代码剥离打包出来的包会更小

缺点:

①可能会错误剥离一些代码(不过这个缺点是可以通过一些方法来弥补的)

②无法在运行时动态生成代码与类型

显然Mono有的主要优点它基本上也有,但是它又比Mono更容易维护且运行效率更高,因此一般能够用IL2CPP就用IL2CPP

5.IL2CPP缺点的弥补

在上面对IL2CPP的了解中,我们知道了IL2CPP的一些缺点与不足,接下来我们会尝试去弥补这些缺点。

5.1.解决类型裁剪

病因:在打包的时候会自动对Unity工程的DLL进行裁剪,将代码中没有用到的部分都给裁剪掉,以使得打包后的程序更小。但是它会不小心把一些看起来没有用到但是实际上是需要的类型给意外裁剪掉

症状:意外裁剪掉之后,在运行的时候我们会遇到【找不到这个类型】的错误

下药:

  • 治标:IL2CPP处理模式的时候把代码剥离等级调为低
    新版本好像还有个Minimal
  • 治本:通过在link.xml文件中告诉Unity哪些类型不能够被裁剪(一般搭配High等级的剥离代码使用以把打包的程序压缩到最小)。具体的使用方法如下所示
    <?xml version="1.0" encoding="UTF-8"?><!--保存整个程序集--><assembly fullname="UnityEngine" preserve="all"/><!--没有“preserve”属性,也没有指定类型意味着保留所有--><assembly fullname="UnityEngine"/><!--完全限定程序集名称--><assembly fullname="Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"><type fullname="Assembly-CSharp.Foo" preserve="all"/></assembly><!--在程序集中保留类型和成员--><assembly fullname="Assembly-CSharp"><!--保留整个类型--><type fullname="MyGame.A" preserve="all"/><!--没有“保留”属性,也没有指定成员 意味着保留所有成员--><type fullname="MyGame.B"/><!--保留类型上的所有字段--><type fullname="MyGame.C" preserve="fields"/><!--保留类型上的所有方法--><type fullname="MyGame.D" preserve="methods"/><!--只保留类型--><type fullname="MyGame.E" preserve="nothing"/><!--仅保留类型的特定成员--><type fullname="MyGame.F"><!--类型和名称保留--><field signature="System.Int32 field1" /><!--按名称而不是签名保留字段--><field name="field2" /><!--方法--><method signature="System.Void Method1()" /><!--保留带有参数的方法--><method signature="System.Void Method2(System.Int32,System.String)" /><!--按名称保留方法--><method name="Method3" /><!--属性--><!--保留属性--><property signature="System.Int32 Property1" /><property signature="System.Int32 Property2" accessors="all" /><!--保留属性、其支持字段(如果存在)和getter方法--><property signature="System.Int32 Property3" accessors="get" /><!--保留属性、其支持字段(如果存在)和setter方法--><property signature="System.Int32 Property4" accessors="set" /><!--按名称保留属性--><property name="Property5" /><!--事件--><!--保存事件及其支持字段(如果存在),添加和删除方法--><event signature="System.EventHandler Event1" /><!--根据名字保留事件--><event name="Event2" /></type><!--泛型相关保留--><type fullname="MyGame.G`1"><!--保留带有泛型的字段--><field signature="System.Collections.Generic.List`1&lt;System.Int32&gt; field1" /><field signature="System.Collections.Generic.List`1&lt;T&gt; field2" /><!--保留带有泛型的方法--><method signature="System.Void Method1(System.Collections.Generic.List`1&lt;System.Int32&gt;)" /><!--保留带有泛型的事件--><event signature="System.EventHandler`1&lt;System.EventArgs&gt; Event1" /></type><!--如果使用类型,则保留该类型的所有字段。如果类型不是用过的话会被移除--><type fullname="MyGame.I" preserve="fields" required="0"/><!--如果使用某个类型,则保留该类型的所有方法。如果未使用该类型,则会将其删除--><type fullname="MyGame.J" preserve="methods" required="0"/><!--保留命名空间中的所有类型--><type fullname="MyGame.SomeNamespace*" /><!--保留名称中带有公共前缀的所有类型--><type fullname="Prefix*" /></assembly></linker>

    一般我们主要指定特定的命名空间或者特定的类不要被裁剪掉就行了

5.2.解决泛型问题

病因:IL2CPP在运行的时候无法动态生成类型和代码。

症状:如果打包生成前没有把之后没有想要使用的泛型类型显式地使用一次,那么这个泛型类型就不会被编译,这会导致之后使用没有被编译的类型就会出现【找不到类型】的报错

下药:

  • 泛型类:声明一个类,然后在这个类中声明一些public的泛型变量
  • 泛型方法:随便写一个静态方法,在这个泛型方法中调用一次这个泛型类型。(这个方法可以不被调用,这个只是为了告诉IL2CPP我们需要用这个泛型类型。

四.总结

①Unity的底层是C++,上层是C#等语言编写的,因此有跨语言的需求。Unity希望用户编写的工程程序能够在多个平台上运行,因此有跨平台的需求。而Mono与Unity诞生时间差不多,且能够满足跨语言和跨平台的需求,因此被Unity采用

②Mono是通过把语言都编译为CIL公共中间语言实现跨语言的操作(基于.Net Framework的功能),之后通过对应平台上的Mono VM(虚拟机)把CIL转化为对应平台的可执行机器码。

③Mono的优点在于打包快,理论上可以支持无限多的平台;缺点在于执行效率相对AOT的较慢(Mono是JIT的),维护各个平台的虚拟机麻烦,且每次更新版本都要同步更新全部平台的虚拟机

④IL2CPP是通过把语言编译为CIL之后通过IL2CPP应用程序转化为C++,然后再通过各个平台上都有的优化过的C++编译器编译为对应平台的机器码,不过由于内存管理还是采用C#的方式,因此还需要通过IL2CPP VM(虚拟机)去完成内存管理,线程分配等工作。

⑤IL2CPP优点是执行效率相比Mono更高(因为是AOT的),且会自动剥离无用的代码减小程序的体积。缺点是无法运行的时候生成类型与代码(因为是AOT),且可能会不小心剥离掉其实是需要的类的代码

⑥对于IL2CPP的缺点,我们可以对应进行处理。

  • 对于类型裁剪的问题,我们主要可以通过调低剥离程度或者使用link.xml指定不允许剥离哪些类
  • 对于泛型的问题,我们可以需要的把泛型类和方法都定义在一个类中,并在一个静态方法中显式地调用它们一次

⑦在正常情况下,我们更推荐使用IL2CPP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2800.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode:无重复字符的最长子串

目录 解题过程: 描述: 分析条件: 正确解题思路: 通过这道题可以学到什么: 解题过程: 描述: 3. 无重复字符的最长子串 提示 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为…

JUC读写锁

文章目录一、读写锁概述1.1 核心目标1.2 核心思想1.3 关键规则与保证1.4 核心组件二、使用示例2.1 采用独占锁的姿势读、写数据2.2 使用读写锁读、写数据2.3 锁降级 **&#xff08;Lock Downgrading&#xff09;**三、应用场景3.1 缓存系统【高频读、低频更新】3.2 配置中心【配…

docker compose再阿里云上无法使用的问题

最原始的Dokcerfile # 使用官方Python 3.6.8镜像 FROM python:3.6.8-slimWORKDIR /app# 复制依赖文件 COPY requirements.txt .RUN pip install --upgrade pip # 检查并安装依赖&#xff08;自动处理未安装的包&#xff09; RUN pip install --no-cache-dir -r requirements.tx…

【运维进阶】LNMP + WordPress 自动化部署实验

LNMP WordPress 自动化部署实验 一、实验目标 通过 Ansible 自动化工具&#xff0c;在目标服务器&#xff08;lnmp 主机组&#xff09;上搭建 LNMP 架构&#xff08;Linux 系统 Nginx 网页服务器 MariaDB 数据库 PHP 脚本语言&#xff09;&#xff0c;并部署 WordPress 博…

豆包 Java的23种设计模式

Java的23种设计模式是软件开发中常用的设计思想总结&#xff0c;根据用途可分为三大类&#xff1a;创建型、结构型和行为型。 一、创建型模式&#xff08;5种&#xff09; 用于处理对象创建机制&#xff0c;隐藏创建逻辑&#xff0c;使程序更灵活。 单例模式&#xff1a;保证一…

RISC-V汇编新手入门

有空就更。一、基础核心概念&#xff1a;什么是汇编语言&#xff1f;汇编语言是直接对应 CPU 指令的低级编程语言&#xff0c;每一行汇编代码基本对应一条 CPU 能直接执行的指令。相比 C 语言等高级语言&#xff0c;汇编更贴近硬件&#xff0c;能直接操作 CPU 的寄存器、内存和…

[每周一更]-(第155期):Go 1.25 发布:新特性、技术思考与 Go vs Rust 竞争格局分析

作为一名 Go 研发工程师&#xff0c;我一直关注 Go 语言的演进。2025 年 8 月 12 日&#xff0c;Go 团队发布了 Go 1.25 版本&#xff0c;这是继 Go 1.24 之后的又一重要更新。 这个版本聚焦于工具链优化、运行时改进和实验性功能引入&#xff0c;没有引入破坏性语言变化&#…

【网络安全】Webshell的绕过——绕过动态检测引擎WAF-缓存绕过(Hash碰撞)

目录 一、前言 二、环境 三、了解动态检测引擎 3.1 shuffle — 打乱数组 3.2 mt_srand — 播下一个更好的随机数发生器种子 四、缓存导致的绕过【hash碰撞】 五、总结 一、前言 在渗透测试过程中&#xff0c;成功获取 WebShell 时难免遇到 Web 应用防火墙&#xff08;WA…

【Linux | 网络】高级IO

一、IO是什么二、五种IO模型2.1 理解五种IO模型2.2 五种IO模型的定义三、 非阻塞IO3.1 fcntl函数3.2 实现函数SetNoBlock&#xff08;将文件描述符设置为非阻塞&#xff09;四、多路转接IO4.1 多路转接IO之select4.1.1 select函数4.1.2 select的优缺点4.2 多路转接IO之poll4.2.…

图解简单选择排序C语言实现

1 简单选择排序 简单选择排序&#xff08;Simple Selection Sort&#xff09;是一种基础且直观的排序算法&#xff0c;其核心思想是通过重复选择未排序部分中的最小&#xff08;或最大&#xff09;元素&#xff0c;并将其放到已排序部分的末尾&#xff0c;逐步完成整个序列的排…

[go] 桥接模式

桥接模式 是一种结构型设计模式&#xff0c; 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&#xff0c; 从而能在开发时分别使用。 模型说明抽象部分&#xff08;Abstraction&#xff09;提供高层控制逻辑&#xff0c;依赖于完成底层实际工作的实现对象…

【自用】JavaSE--特殊文件Properties与XML、日志技术

特殊文件概述使用特殊文件可以存储多个有关系的数据&#xff0c;作为系统的配置信息属性文件类似于键值对&#xff0c;一一对应存储数据(比如用户名与密码)XML文件存储多个用户的多个属性更适合&#xff0c;适合存储更复杂的数据Properties注&#xff1a;这个属性文件的后缀即使…