版本:Unity 6(6000.0)
语言:英语
iOS 上的托管堆栈跟踪
针对 iOS 报告崩溃错误

iOS 设备故障排除

使用以下信息解决在 iOS 设备中运行 Unity 应用程序时常见的崩溃和其他问题。

注意:如果问题在故障排除后仍然存在,请参见针对 iOS 报告崩溃错误

应用程序停止响应,Xcode 在状态栏中显示中断

以下列表提供了一些导致此问题的原因

  • 脚本错误,例如使用未初始化变量。
  • 使用第三方 Thumb 编译的本机库。这些库可能会触发 iOS SDK 链接器中的已知问题,并引发无规律的崩溃。
  • 使用以值类型作为参数的泛型类型。例如:List<int>List<SomeStruct>List<SomeEnum>(用于已序列化的脚本属性)。
  • 在启用托管代码剥离时使用反射。
  • 托管代码方法签名与本机代码函数签名不匹配的本机插件在 Unity 外部创建的代码,可在 Unity 中创建功能。有两种类型的插件可用于 Unity:托管插件(使用 Visual Studio 等工具创建的托管 .NET 程序集)和本机插件(特定于平台的本机代码库)。更多信息
    词汇表 中查看
    接口错误。

Xcode 调试器控制台的信息通常可帮助检测这些问题。从视图 > 调试区域 > 激活控制台访问调试器控制台。

Xcode 控制台显示程序收到信号:“SIGBUS”或 EXC_BAD_ACCESS 错误

当您的应用程序收到 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 ()

此消息表明本机符号在应用程序的发布版本中被剥离。要进一步调查,请使用以下步骤访问完整的堆栈跟踪

  1. 从设备中移除应用程序。
  2. 清除所有目标。
  3. 选择 Product > Run
  4. 按照前面描述的步骤获取堆栈跟踪。

Xcode 控制台显示警告->applicationDidReceiveMemoryWarning(),然后应用程序崩溃

您可能会遇到这样的警告消息:Program received signal: "0"。此警告消息通常不是致命的,它表明 iOS 内存在不足。通常,邮件等后台进程会腾出一些内存,您的应用程序可以继续运行。但是,如果您的应用程序继续使用内存或请求更多内存,iOS 将开始终止应用程序,包括您自己的应用程序。Apple 并未记录哪些内存使用是安全的,但观察表明,使用不到全部设备 RAM 的 50% 的应用程序没有重大的内存使用问题。

要使用的主要衡量标准是您的应用程序使用了多少 RAM。您的应用程序内存使用情况由以下组件组成

组件 说明
应用程序代码 操作系统需要加载您的应用程序代码并将其保留在 RAM 中,但如果需要,可能会丢弃其中的一部分。
本机堆 由引擎在 RAM 中存储其状态和资产。
托管堆 由您的 il2cpp您可以在构建针对一些平台的项目时用作 Mono 的备选方案的 Unity 开发的脚本后端。 更多信息
参见 词汇表
运行时存储 C# 对象。
Metal 内存池 用于存储纹理、帧缓冲区和已编译的 着色器在 GPU 上运行的程序。 更多信息
参见 术语表

您可以在 Xcode 中跟踪应用程序内存使用情况。有关更多信息,请参阅 收集有关内存使用情况的信息(Apple Developer)。

为了尽量减少内存使用量,请使用以下建议

  • 减少应用程序二进制文件大小:使用最严格的 iOS 剥离选项,并避免对不同的 .NET 库产生不必要的依赖。有关更多信息,请参阅 Player 设置和 优化生成的 iOS Player 大小
  • 减小内容大小:对纹理使用 PVRTC 压缩一种存储数据的方法,可减少其所需存储空间量。 请参阅 纹理压缩动画压缩音频压缩生成压缩
    参见 术语表
    ,并使用低面数模型。有关更多信息,请参阅 减少文件大小
  • 脚本一段允许您创建自己的组件、触发游戏事件、随着时间的推移修改组件属性以及以您希望的任何方式响应用户输入的代码。 更多信息
    参见 术语表
    中不要分配超过必要的内存:使用 Profiler 窗口来跟踪单声道堆大小和使用情况。

查询操作系统有关空闲内存量的信息可能看起来是评估应用程序执行情况的最有效方法。但是,空闲内存统计数据可能不可靠,因为操作系统会使用大量的动态缓冲区和缓存。建议您跟踪应用程序的内存消耗情况,并将其用作结合 Xcode 内存工具(尤其是在加载新 场景场景包含游戏的环境和菜单。将每个独特的场景文件看作独特的关卡。在每个场景中,您可以放置环境、障碍物和装饰,基本上是分块设计和构建游戏。 更多信息
参见 术语表
之后)的主要指标。

应用程序通过 Xcode 启动正确,但在设备上加载第一个场景时崩溃

建议检查设备日志以获取更多详细信息。要执行此操作,请使用以下步骤

  1. 将目标设备连接到 macOS 设备。
  2. 在 Xcode 中,选择 窗口 > 设备和模拟器
  3. 在窗口的左侧工具栏Unity 编辑器顶部的按钮和基本控件行,允许您通过各种方式与编辑器进行交互(例如缩放、转换)。更多信息
    请参见词汇表
    中选择您的目标设备。
  4. 单击显示设备控制台并查看最新消息。

您可能还需要调查故障报告。如需更多信息,请参阅获得故障报告和诊断日志(Apple Developer)。

Xcode Organizer 控制台中出现由 SpringBoard 终止

设置一个时间限制,允许 iOS 应用程序渲染第一帧并处理输入。如果应用程序超过此限制,它会被 SpringBoard 终止。例如,这可能发生在第一个场景太大​​的应用程序中。建议创建带有启动屏幕的小型初始场景,等待几帧,然后继续加载较大的场景。为实现此目的,请使用以下示例

IEnumerator Start() {
    yield return new WaitForEndOfFrame();
// Do not forget using UnityEngine.SceneManagement directive
    SceneManager.LoadScene("Test");
}

使用 System.Security.Cryptography 和托管代码剥离时设备发生崩溃

.NET 密码服务与托管代码分割不兼容。这些服务依赖于反射,而托管代码分割涉及静态代码分析。可通过将自定义link.xml文件添加到 Unity 项目的Assets文件夹中来自定义分割过程。这指定了从分割中排除的类型和命名空间。将System.Security.Crypography命名空间从分割过程中排除,以帮助解决此问题。例如,将以下内容添加到link.xml文件

<linker>
       <assembly fullname="mscorlib">
               <namespace fullname="System.Security.Cryptography" preserve="all"/>
       </assembly>
</linker>

使用 System.Security.Cryptography.MD5 和托管代码剥离时应用程序崩溃

您可以按照上一部分中概述的方式解决此问题,或者可以在脚本代码中添加对特定类的额外引用。为实现此目的,请使用以下示例

object obj = new MD5CryptoServiceProvider();

PlayerLoop 不断递归调用!使用本机函数通过 Cocoa 时出现错误消息

UI(用户界面)允许用户与您的应用程序进行交互。Unity 目前支持三个 UI 系统。更多信息
请参见词汇表
中的一些操作会导致 iOS 立即重绘窗口。最常见的示例是将带有 UIViewController 的 UIView 添加到主 UIWindow。如果从脚本调用本机函数,它将在 Unity 的 PlayerLoop 中发生,导致 PlayerLoop 被递归调用。发生这种情况时,您将收到错误消息PlayerLoop called recursively!。在这种情况下,请考虑使用performSelectorOnMainThread(Apple Developer)方法,并将waitUntilDone设置为false。它将通知 iOS 安排操作在 Unity 的 PlayerLoop 调用之间运行。

分析器或调试器无法访问应用程序

为诊断此问题,请使用以下建议

  • 检查你已经构建了开发构建开发构建包含调试符号并启用 Profiler。 更多信息
    请参见 词汇表
    ,启用了脚本调试并启用了自动连接 Profiler一个可帮助你优化游戏的窗口。它显示了游戏中各个区域花费的时间。例如,它可以报告渲染、动画或游戏逻辑中所花费时间的百分比。 更多信息
    请参见 词汇表
    。有关这些属性的更多信息,请参见 iOS 构建设置参考
  • 在设备上运行的应用程序将采用 UDP 端口54997向多播组播到225.0.0.222。检查你的网络设置是否允许此流量。Profiler 还将在范围55000 - 55511内的某个端口通过远程设备建立连接,以从设备获取 Profiler 数据。这些端口需要对 TCP 访问处于开放状态。

Xcode 显示错误 ARM64 分支超出范围(<value> 最大为 +/–128MB)

此问题是由于构建的机器码太大并达到 Xcode 限制所致。如果你有大量的脚本代码,或者在构建中使用很大的外部 .NET 程序集,则可能发生这种情况。使用脚本调试构建设置也可能加剧此问题,因为它为每个函数创建了其他指令。

为协助解决此问题,在 Unity 编辑器中导航到编辑>项目设置>播放器>iOS,然后尝试以下一个或多个选项

  • 启用精简引擎代码
  • 使用较高的托管精简级别
  • IL2CPP 代码生成设置为更快(更小)的构建

如果问题仍然存在,建议将用户脚本代码拆分为多个程序集。例如,Plugins 文件夹可用于放置任何拆分的代码,因为该位置的代码会添加到不同的程序集中。还建议参阅 特殊文件夹和脚本编译顺序 以了解特殊文件夹名称如何影响脚本编译。

运行 Xcode 14.3 的基于 ARM 的 Mac 上看不见 iOS 模拟器

从 Xcode 14.3 版本开始,Apple 引入了目标架构选项。使用目标架构,你可以使用基于 ARM 的 Mac 上的 iOS 模拟器,而无需在 Rosetta 模拟器模式下运行 Xcode。

要查看 iOS 模拟器,请按照以下步骤操作

  1. 在 Xcode 菜单栏中,选择产品>目标>目标架构
  2. 选择显示 Rosetta 目标同时显示,以查看 Apple Silicon 和 Rosetta 架构的 iOS 模拟器。

其他资源

iOS 上的托管堆栈跟踪
针对 iOS 报告崩溃错误