Version: 6000.3
语言: 中文
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.使用本机堆栈跟踪来找出故障发生的位置。

本机堆栈跟踪是故障调查的有用工具,但使用它们需要一些专业知识。发生这些硬件内存访问故障后,您通常无法继续。要访问本机堆栈跟踪,请键入bt all到 Xcode 调试器控制台中。检查打印的堆栈跟踪,因为它们可能包含有关错误发生位置的信息。例如,典型的堆栈跟踪可能如下所示:

...
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 ()
...

找到“线程 1”的堆栈跟踪,这是主线程。堆栈跟踪的初始行将指向发生错误的位置。在此示例中,跟踪指示NullReferenceException发生在_OptionsMenu_脚本的_Start_方法。检查此方法实现有助于揭示问题的原因。通常,NullReferenceExceptions 发生在_Start_方法,当对初始化顺序做出不正确的假设时。

有时,调试器控制台中仅显示部分堆栈跟踪。例如:

Thread 1 (thread 11523): 

1. 0 0x0062564c in start ()

此消息指示在应用程序的发布生成期间剥离了本机符号。若要进一步调查,请使用以下步骤访问全栈跟踪:

  1. 从设备中删除应用程序。
  2. 清理所有目标。
  3. 选择“产品>运行”
  4. 如前所述检索堆栈跟踪。

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

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

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

元件 描述
应用程序代码 作系统需要将应用程序代码加载并保存在 RAM 中,但如果需要,其中一些代码可能会被丢弃。
本机堆 引擎用于将其状态和资产存储在RAM中。
托管堆 由您的il2cppUnity 开发的脚本后端,在为某些平台构建项目时,您可以将其用作 Mono 的替代品。更多信息
请参阅术语表
运行时来存储 C# 对象。
金属内存池 用于存储纹理、帧缓冲区和编译的纹理着色器在 GPU 上运行的程序。更多信息
请参阅术语表
.

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

若要保持较低的内存使用率,请使用以下建议:

向作系统查询可用内存量似乎是评估应用程序性能的最有效方法。但是,由于作系统使用大量动态缓冲区和缓存,因此可用内存统计数据可能不可靠。建议跟踪应用程序的内存消耗并将其用作主要指标,并结合 Xcode 内存工具,尤其是在加载新的场景场景包含游戏的环境和菜单。将每个唯一的场景文件视为一个独特的关卡。在每个场景中,你放置你的环境、障碍物和装饰品,基本上是将你的游戏设计和构建成碎片。更多信息
请参阅术语表
.

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

建议检查设备日志以获取更多详细信息。为此,请使用以下步骤:

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

您可能还需要调查崩溃报告。有关详细信息,请参阅获取崩溃报告和诊断日志 (Apple Developer)。

Killed by SpringBoard 显示在 Xcode 管理器控制台中

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

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

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

.NET 加密服务与托管代码剥离不兼容。这些服务依赖于反射,而托管代码剥离涉及静态代码分析。剥离工艺可以通过添加自定义link.xml文件到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 中的某些作将导致 iOS 立即重新绘制窗口。最常见的示例是将带有 UIViewController 的 UIView 添加到主 UIWindow。如果从脚本调用本机函数,它将发生在 Unity 的 PlayerLoop 中,从而导致递归调用 PlayerLoop。发生这种情况时,您将收到错误消息PlayerLoop called recursively!.在这种情况下,请考虑将 performSelectorOnMainThread (Apple Developer) 方法waitUntilDone设置为 false。它将通知 iOS 安排作在 Unity 的 PlayerLoop 调用之间运行。

探查器或调试器无法访问应用程序

若要诊断此问题,请使用以下建议:

  • 检查您是否已构建开发版本开发版本包括调试符号并启用性能分析器。更多信息
    请参阅术语表
    、启用脚本调试自动连接分析器帮助您优化游戏的窗口。它显示了在游戏的各个领域花费了多少时间。例如,它可以报告渲染、动画制作或游戏逻辑所花费的时间百分比。更多信息
    请参阅术语表
    .有关这些属性的更多信息,请参阅 iOS 构建设置参考
  • 设备上运行的应用程序将进行多播广播225.0.0.222在 UDP 端口上54997.检查您的网络设置是否允许此流量。分析器还将连接到范围内端口上的远程设备55000 - 55511从设备获取分析器数据。这些端口需要打开才能进行 TCP 访问。

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

当构建的机器代码太大并达到 Xcode 限制时,会导致此问题。如果有很多脚本代码,或者在生成中使用大型外部 .NET 程序集,则可能会发生这种情况。使用脚本调试构建设置也会加剧此问题,因为它会为每个函数创建额外的指令。

为了帮助解决此问题,请在 Unity 编辑器中导航到 Edit > Project Settings > Player > iOS,然后尝试以下一个或多个选项:

  • 启用剥离引擎代码
  • 使用更高的托管剥离级别
  • IL2CPP 代码生成设置为针对代码大小和构建时间进行优化

如果问题仍然存在,建议将用户脚本代码拆分为多个程序集。例如,Plugins文件夹可用于放置任何拆分代码,因为此位置的代码将添加到其他程序集。还建议参考特殊文件夹和脚本编译顺序,了解有关特殊文件夹名称如何影响脚本编译的信息。

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

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

要查看 iOS 模拟器,请使用以下步骤:

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

其他资源

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