使用以下信息解决在 iOS 设备中运行 Unity 应用程序时常见的崩溃和其他问题。
注意:如果问题在故障排除后仍然存在,请参见针对 iOS 报告崩溃错误。
以下列表提供了一些导致此问题的原因
List<int>
、List<SomeStruct>
、List<SomeEnum>
(用于已序列化的脚本属性)。Xcode 调试器控制台的信息通常可帮助检测这些问题。从视图 > 调试区域 > 激活控制台访问调试器控制台。
当您的应用程序收到 NullReferenceException
时,该消息通常会出现在 iOS 设备上。使用本机堆栈跟踪来找出故障发生的位置。
本机堆栈跟踪是故障调查的一个有用工具,但是使用它们需要一定的专业知识。在这些硬件内存访问故障发生之后,您通常无法继续。要访问本机堆栈跟踪,请在 Xcode 调试器控制台中键入 bt all
。检查打印的堆栈跟踪,因为它们可能包含有关错误发生位置的信息。例如,典型的堆栈跟踪可能如下所示
...
Thread 1 (thread 11523):
1. 0 0x006267d0 in m_OptionsMenu_Start ()
1. 1 0x002e4160 in wrapper_runtime_invoke_object_runtime_invoke_void__this___object_intptr_intptr_intptr ()
1. 2 0x00a1dd64 in mono_jit_runtime_invoke (method=0x18b63bc, obj=0x5d10cb0, params=0x0, exc=0x2fffdd34) at /Users/mantasp/work/unity/unity-mono/External/Mono/mono/mono/mini/mini.c:4487
1. 3 0x0088481c in MonoBehaviour::InvokeMethodOrCoroutineChecked ()
...
找到 “Thread 1” 的堆栈跟踪,它是主线程。堆栈跟踪的初始行将指向错误发生的位置。在此示例中,跟踪表明 NullReferenceException
发生在 _OptionsMenu_
脚本的 _Start_
方法中。检查此方法实现有助于发现问题的根源。通常,当对初始化顺序做出不正确的假设时,NullReferenceExceptions 会发生在 _Start_
方法中。
有时调试器控制台中只会显示部分堆栈跟踪。例如
Thread 1 (thread 11523):
1. 0 0x0062564c in start ()
此消息表明本机符号在应用程序的发布版本中被剥离。要进一步调查,请使用以下步骤访问完整的堆栈跟踪
您可能会遇到这样的警告消息:Program received signal: "0"
。此警告消息通常不是致命的,它表明 iOS 内存在不足。通常,邮件等后台进程会腾出一些内存,您的应用程序可以继续运行。但是,如果您的应用程序继续使用内存或请求更多内存,iOS 将开始终止应用程序,包括您自己的应用程序。Apple 并未记录哪些内存使用是安全的,但观察表明,使用不到全部设备 RAM 的 50% 的应用程序没有重大的内存使用问题。
要使用的主要衡量标准是您的应用程序使用了多少 RAM。您的应用程序内存使用情况由以下组件组成
组件 | 说明 |
---|---|
应用程序代码 | 操作系统需要加载您的应用程序代码并将其保留在 RAM 中,但如果需要,可能会丢弃其中的一部分。 |
本机堆 | 由引擎在 RAM 中存储其状态和资产。 |
托管堆 | 由您的 il2cpp您可以在构建针对一些平台的项目时用作 Mono 的备选方案的 Unity 开发的脚本后端。 更多信息 参见 词汇表 运行时存储 C# 对象。 |
Metal 内存池 | 用于存储纹理、帧缓冲区和已编译的 着色器在 GPU 上运行的程序。 更多信息 参见 术语表。 |
您可以在 Xcode 中跟踪应用程序内存使用情况。有关更多信息,请参阅 收集有关内存使用情况的信息(Apple Developer)。
为了尽量减少内存使用量,请使用以下建议
查询操作系统有关空闲内存量的信息可能看起来是评估应用程序执行情况的最有效方法。但是,空闲内存统计数据可能不可靠,因为操作系统会使用大量的动态缓冲区和缓存。建议您跟踪应用程序的内存消耗情况,并将其用作结合 Xcode 内存工具(尤其是在加载新 场景场景包含游戏的环境和菜单。将每个独特的场景文件看作独特的关卡。在每个场景中,您可以放置环境、障碍物和装饰,基本上是分块设计和构建游戏。 更多信息
参见 术语表之后)的主要指标。
建议检查设备日志以获取更多详细信息。要执行此操作,请使用以下步骤
您可能还需要调查故障报告。如需更多信息,请参阅获得故障报告和诊断日志(Apple Developer)。
设置一个时间限制,允许 iOS 应用程序渲染第一帧并处理输入。如果应用程序超过此限制,它会被 SpringBoard 终止。例如,这可能发生在第一个场景太大的应用程序中。建议创建带有启动屏幕的小型初始场景,等待几帧,然后继续加载较大的场景。为实现此目的,请使用以下示例
IEnumerator Start() {
yield return new WaitForEndOfFrame();
// Do not forget using UnityEngine.SceneManagement directive
SceneManager.LoadScene("Test");
}
.NET 密码服务与托管代码分割不兼容。这些服务依赖于反射,而托管代码分割涉及静态代码分析。可通过将自定义link.xml
文件添加到 Unity 项目的Assets
文件夹中来自定义分割过程。这指定了从分割中排除的类型和命名空间。将System.Security.Crypography
命名空间从分割过程中排除,以帮助解决此问题。例如,将以下内容添加到link.xml
文件
<linker>
<assembly fullname="mscorlib">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
</linker>
您可以按照上一部分中概述的方式解决此问题,或者可以在脚本代码中添加对特定类的额外引用。为实现此目的,请使用以下示例
object obj = new MD5CryptoServiceProvider();
UI(用户界面)允许用户与您的应用程序进行交互。Unity 目前支持三个 UI 系统。更多信息
请参见词汇表中的一些操作会导致 iOS 立即重绘窗口。最常见的示例是将带有 UIViewController 的 UIView 添加到主 UIWindow。如果从脚本调用本机函数,它将在 Unity 的 PlayerLoop 中发生,导致 PlayerLoop 被递归调用。发生这种情况时,您将收到错误消息PlayerLoop called recursively!
。在这种情况下,请考虑使用performSelectorOnMainThread(Apple Developer)方法,并将waitUntilDone
设置为false。它将通知 iOS 安排操作在 Unity 的 PlayerLoop 调用之间运行。
为诊断此问题,请使用以下建议
54997
向多播组播到225.0.0.222
。检查你的网络设置是否允许此流量。Profiler 还将在范围55000 - 55511
内的某个端口通过远程设备建立连接,以从设备获取 Profiler 数据。这些端口需要对 TCP 访问处于开放状态。此问题是由于构建的机器码太大并达到 Xcode 限制所致。如果你有大量的脚本代码,或者在构建中使用很大的外部 .NET 程序集,则可能发生这种情况。使用脚本调试构建设置也可能加剧此问题,因为它为每个函数创建了其他指令。
为协助解决此问题,在 Unity 编辑器中导航到编辑>项目设置>播放器>iOS,然后尝试以下一个或多个选项
如果问题仍然存在,建议将用户脚本代码拆分为多个程序集。例如,Plugins
文件夹可用于放置任何拆分的代码,因为该位置的代码会添加到不同的程序集中。还建议参阅 特殊文件夹和脚本编译顺序 以了解特殊文件夹名称如何影响脚本编译。
从 Xcode 14.3 版本开始,Apple 引入了目标架构选项。使用目标架构,你可以使用基于 ARM 的 Mac 上的 iOS 模拟器,而无需在 Rosetta 模拟器模式下运行 Xcode。
要查看 iOS 模拟器,请按照以下步骤操作