Version: 2017.2
Unity iOS 目前不支持的功能
报告 iOS 上的崩溃错误

在 iOS 设备上进行故障排除

在 iOS 中,有些情况下,游戏可以在 Unity Editor 中完美运行,但在实际设备上却无法运行,甚至无法启动。 这些问题通常与代码或内容质量有关。本部分将介绍最常见的情况。

游戏在片刻之后停止响应。Xcode 在状态栏中显示“interrupted”。

出现这种情况的原因有很多。典型原因包括:

1.脚本错误,例如使用未初始化的变量等。 1.使用了第三方 Thumb 编译的原生库。这些库会在 iOS SDK 链接器中触发一个已知问题,并可能导致随机崩溃。 1. Using generic types with value types as parameters (eg, List<int>, List<SomeStruct>, List<SomeEnum>, etc) for serializable script properties. 1.在启用托管代码剥离的情况下使用了反射。 1.原生插件接口中出错(托管代码方法签名与原生代码函数签名不匹配)。 来自 XCode 调试器控制台的信息通常有助于检测这些问题。(Xcode 菜单:__View > Debug Area > Activate Console__)。

Xcode 控制台显示 Program received signal: “SIGBUS” 或 EXC_BAD_ACCESS 错误。

This message typically appears on iOS devices when your application receives a NullReferenceException. There two ways to figure out where the fault happened:

托管堆栈跟踪

Unity includes software-based handling of the NullReferenceException. The AOT compiler includes quick checks for null references each time a method or variable is accessed on an object. This feature affects script performance which is why it is enabled only for development builds (enable the “script debugging” option in build settings dialog). If everything was done right and the fault actually is occurring in .NET code then you won’t see EXC_BAD_ACCESS anymore. Instead, the .NET exception text will be printed in the Xcode console (or else your code will just handle it in a “catch” statement). Typical output might be:

Unhandled Exception: System.NullReferenceException: A null value was found where an object instance was required.
  at DayController+$handleTimeOfDay$121+$.MoveNext () [0x0035a] in DayController.js:122

This indicates that the fault happened in the handleTimeOfDay method of the DayController class, which works as a coroutine. Also if it is script code then you will generally be told the exact line number (eg, “DayController.js:122”). The offending line might be something like the following:

 Instantiate(_imgwww.assetBundle.mainAsset);

比如,如果脚本没有事先检查其 Asset Bundle 是否下载正确而直接访问该 Asset Bundle,则可能会发生这种情况。

原生堆栈跟踪

Native stack traces are a much more powerful tool for fault investigation but using them requires some expertise. Also, you generally can’t continue after these native (hardware memory access) faults happen. To get a native stack trace, type bt all into the Xcode Debugger Console. Carefully inspect the printed stack traces - they may contain hints about where the error occurred. You might see something like:

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

First of all you should find the stack trace for “Thread 1”, which is the main thread. The very first lines of the stack trace will point to the place where the error occurred. In this example, the trace indicates that the NullReferenceException happened inside the “OptionsMenu” script’s “Start” method. Looking carefully at this method implementation would reveal the cause of the problem. Typically, NullReferenceExceptions happen inside the Start method when incorrect assumptions are made about initialization order. In some cases only a partial stack trace is seen on the Debugger Console:

Thread 1 (thread 11523): 

1.0 0x0062564c in start ()

此信息表明原生符号在应用程序的发行版构建过程中被剥离。可以通过以下过程获得完整的堆栈跟踪:

  • 从设备上删除应用程序。
  • 清除所有目标。
  • 构建并运行。
  • 如前所述,再次获取堆栈跟踪。

当外部库链接到 Unity iOS 应用程序时,便开始发生 EXC_BAD_ACCESS。

This usually happens when an external library is compiled with the ARM Thumb instruction set. Currently such libraries are not compatible with Unity. The problem can be solved easily by recompiling the library without Thumb instructions. You can do this for the library’s Xcode project with the following steps:

  • in Xcode, select “View” > “Navigators” > “Show Project Navigator” from the menu
  • select the “Unity-iPhone” project, activate “Build Settings” tab
  • in the search field enter : “Other C Flags”
  • add -mno-thumb flag there and rebuild the library.

如果库来源不明,则应要求供应商提供库的非 Thumb 版本。

Xcode 控制台显示“WARNING -> applicationDidReceiveMemoryWarning()”,而且应用程序随后立即崩溃

(Sometimes you might see a message like Program received signal: “0”.) This warning message is often not fatal and merely indicates that iOS is low on memory and is asking applications to free up some memory. Typically, background processes like Mail will free some memory and your application can continue to run. However, if your application continues to use memory or ask for more, the OS will eventually start killing applications and yours could be one of them. Apple does not document what memory usage is safe, but empirical observations show that applications using less than 50% MB of all device RAM (roughly 200–256 MB for 2nd generation ipad) do not have major memory usage problems. The main metric you should rely on is how much RAM your application uses. Your application memory usage consists of three major components:

  • application code (the OS needs to load and keep your application code in RAM, but some of it might be discarded if really needed)
  • native heap (used by the engine to store its state, your assets, etc. in RAM)
  • managed heap (used by your Mono runtime to keep C# or JavaScript objects)
  • GLES driver memory pools: textures, framebuffers, compiled shaders, etc. Your application memory usage can be tracked by two Xcode Instruments tools: Activity Monitor, Object Allocations and VM Tracker. You can start from the Xcode Run menu: Product > Profile and then select specific tool. Activity Monitor tool shows all process statistics including Real memory which can be regarded as the total amount of RAM used by your application. Note: OS and device HW version combination might noticeably affect memory usage numbers, so you should be careful when comparing numbers obtained on different devices.

注意:内部性能分析器仅显示 .NET 脚本分配的堆。总内存使用量可以通过 Xcode Instruments 来确定,如上所示。此图包括应用程序二进制文件、一些标准框架缓冲区、Unity 引擎内部状态缓冲区、.NET 运行时堆(由内部性能分析器输出的数字)、GLES 驱动程序堆和其他一些杂项内容等多个部分。

The other tool displays all allocations made by your application and includes both native heap and managed heap statistics (don’t forget to check the Created and still living box to get the current state of the application). The important statistic is the Net bytes value.

为了降低内存使用量,请遵循以下原则:

  • Reduce the application binary size by using the strongest iOS stripping options, and avoid unnecessary dependencies on different .NET libraries. See the player settings and player size optimization manual pages for further details.
  • 减小内容的大小。对纹理进行 PVRTC 压缩并使用简单多边形模型。请参阅有关减小文件大小的手册页以了解更多信息。
  • Don’t allocate more memory than necessary in your scripts. Track mono heap size and usage with the internal profiler
  • Note: with Unity 3.0, the scene loading implementation has changed significantly and now all scene assets are preloaded. This results in fewer hiccups when instantiating game objects. If you need more fine-grained control of asset loading and unloading during gameplay, you should use Resources.Load and Object.Destroy.

向操作系统查询可用内存量看起来似乎是评估应用程序性能的好办法。但是,由于操作系统使用了大量的动态缓冲区和缓存,所以可用内存统计信息很可能不可靠。唯一可靠的方法是跟踪应用程序的内存消耗,并将其用作主要指标。注意上述工具中的图是如何随着时间的推移而变化的,特别是在加载新的关卡之后。

游戏从 Xcode 启动时运行正常,但在设备上手动启动时,加载第一个关卡的过程中发生崩溃。

There could be several reasons for this. You need to inspect the device logs to get more details. Connect the device to your Mac, launch Xcode and select Window > Organizer from the menu. Select your device in the Organizer’s left toolbar, then click on the “Console” tab and review the latest messages carefully. Additionally, you may need to investigate crash reports. You can find out how to obtain crash reports here: http://developer.apple.com/iphone/library/technotes/tn2008/tn2151.html.

Xcode Organizer 控制台包含消息“killed by SpringBoard”。

有关 iOS 应用程序渲染其第一帧和处理输入的时间限制,并没有完善的记录。如果应用程序超过此限制,则将被 SpringBoard 终止。例如,如果应用程序中的第一个场景太大,便会发生这种情况。为了避免这个问题,建议创建一个小的初始场景,只显示一个启动画面,使用 yield 等待一两帧,然后开始加载真实场景。可以用简单的代码来实现这一点,如下所示:

function Start() {
    yield;
    Application.LoadLevel("Test");
}

Type.GetProperty() / Type.GetValue() cause crashes on the device

Currently Type.GetProperty() and Type.GetValue() are supported only for the .NET 2.0 Subset profile. You can select the .NET API compatibility level in the Player Settings.

注意:Type.GetProperty()Type.GetValue() 可能与托管代码剥离不兼容,可能需要排除(可以在剥离过程中提供自定义的不可剥离类型列表来实现这一点)。有关更多详细信息,请参阅 iOS 播放器大小优化指南

游戏崩溃并显示错误消息“ExecutionEngineException: Attempting to JIT compile method ‘SometType`1<SomeValueType>:.ctor ()’ while running with –aot-only.”

The Mono .NET implementation for iOS is based on AOT (ahead of time compilation to native code) technology, which has its limitations. It compiles only those generic type methods (where a value type is used as a generic parameter) which are explicitly used by other code. When such methods are used only via reflection or from native code (ie, the serialization system) then they get skipped during AOT compilation. The AOT compiler can be hinted to include code by adding a dummy method somewhere in the script code. This can refer to the missing methods and so get them compiled ahead of time.

void _unusedMethod() {
    var tmp = new SomeType<SomeValueType>();
}

Note: value types are basic types, enums and structs.

将 System.Security.Cryptography 与托管代码剥离组合使用时,设备上发生各种崩溃

.NET Cryptography 服务严重依赖反射,所以不兼容托管代码剥离,因为这涉及静态代码分析。有时,崩溃的最简单解决方案是将整个 System.Security.Crypography 命名空间从剥离过程中排除。

The stripping process can be customized by adding a custom link.xml file to the Assets folder of your Unity project. This specifies which types and namespaces should be excluded from stripping. Further details can be found in the iOS player size optimization guide.

link.xml

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

将 System.Security.Cryptography.MD5 与托管代码剥离一起使用时,应用程序崩溃

You might consider advice listed above or can work around this problem by adding extra reference to specific class to your script code:

object obj = new MD5CryptoServiceProvider();

“Ran out of trampolines of type 0/1/2”运行时错误

This error usually happens if you use lots of recursive generics. You can hint to the AOT compiler to allocate more trampolines of type 0, type 1 or type 2. Additional AOT compiler command line options can be specified in the “Other Settings” section of the Player Settings. For type 1 trampolines, specify nrgctx-trampolines=ABCD, where ABCD is the number of new trampolines required (i.e. 4096). For type 2 trampolines specify nimt-trampolines=ABCD and for type 0 trampolines specify ntrampolines=ABCD.

After upgrading Xcode Unity iOS runtime fails with message “You are using Unity iPhone Basic. You are not allowed to remove the Unity splash screen from your game”

With some latest Xcode releases there were changes introduced in PNG compression and optimization tool. These changes might cause false positives in Unity iOS runtime checks for splash screen modifications. If you encounter such problems try upgrading Unity to the latest publicly available version. If it does not help you might consider following workaround:

  • 从 Unity 进行构建时,请从头开始替换 Xcode 项目(而不是进行追加)

  • Delete already installed project from device

  • Clean project in Xcode (Product->Clean)

  • Clear Xcode’s Derived Data folders (Xcode->Preferences->Locations) If this still does not help try disabling PNG re-compression in Xcode:

  • 打开您的 Xcode 项目

  • Select “Unity-iPhone” project there

  • Select “Build Settings” tab there

  • Look for “Compress PNG files” option and set it to NO

App Store submission fails with “iPhone/iPod Touch: application executable is missing a required architecture. At least one of the following architecture(s) must be present: armv6” message

You might get such message when updating already existing application, which previously was submitted with armv6 support. Unity 4.x and Xcode 4.5 does not support armv6 platform anymore. To solve submission problem just set Target OS Version in Unity Player Settings to 4.3 or higher.

WWW 下载在 Unity Editor 和 Android 中正常工作,但在 iOS 中无效

Most common mistake is to assume that WWW downloads are always happening on separate thread. On some platforms this might be true, but you should not take it for granted. Best way to track WWW status is either to use yield statement or check status in Update method. You should not use busy while loops for that.

通过从脚本调用的原生函数来使用 Cocoa 时,发生“PlayerLoop called recursively!”错误

Some operations with the UI will result in iOS redrawing the window immediately (the most common example is adding a UIView with a UIViewController to the main UIWindow). If you call a native function from a script, it will happen inside Unity’s PlayerLoop, resulting in PlayerLoop being called recursively. In such cases, you should consider using the performSelectorOnMainThread method with waitUntilDone set to false. It will inform iOS to schedule the operation to run between Unity’s PlayerLoop calls.

性能分析器或调试器无法看到游戏在 iOS 设备上运行

  • Check that you have built a Development build, and ticked the “Enable Script Debugging” and “Autoconnect profiler” boxes (as appropriate).
  • 在设备上运行的应用程序将在 UDP 端口 54997 上向 225.0.0.222 进行多播广播。请检查您的网络设置是否允许此流量。然后,性能分析器将连接到远程设备上 55000 到 55511 范围内的端口,以便从设备中获取性能分析器数据。需要打开这些端口来进行 UDP 访问。

缺少 DLL

If your application runs ok in editor but you get errors in your iOS project this may be caused by missing DLLs (e.g. I18N.dll, I19N.West.dll). In this case, try copying those dlls from within the Unity.app to your project’s Assets/Plugins folder. The location of the DLLs within the unity app is: Unity.app/Contents/Frameworks/Mono/lib/mono/unity You should then also check the stripping level of your project to ensure the classes in the DLLs aren’t being removed when the build is optimised. Refer to the iOS Optimisation Page for more information on iOS Stripping Levels.

Xcode 调试器控制台报告:ExecutionEngineException: Attempting to JIT compile method ‘(wrapper native-to-managed) Test:TestFunc (int)’ while running with –aot-only

Typically such message is received when managed function delegate is passed to the native function, but required wrapper code wasn’t generated when building application. You can help AOT compiler by hinting which methods will be passed as delegates to the native code. This can be done by adding “MonoPInvokeCallbackAttribute” custom attribute. Currently only static methods can be passed as delegates to the native code.

代码示例:

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
using AOT;

public class NewBehaviourScript : MonoBehaviour {
    [DllImport ("__Internal")]
    private static extern void DoSomething (NoParamDelegate del1, StringParamDelegate del2);

    delegate void NoParamDelegate ();
    delegate void StringParamDelegate (string str);
    
    [MonoPInvokeCallback(typeof(NoParamDelegate))]
    public static void NoParamCallback() {
        Debug.Log ("Hello from NoParamCallback");
    }
    
    [MonoPInvokeCallback(typeof(StringParamDelegate))]
    public static void StringParamCallback(string str) {
        Debug.Log(string.Format("Hello from StringParamCallback {0}", str));
    }

    // 用于进行初始化
    void Start() {
        DoSomething(NoParamCallback, StringParamCallback);
    }
}

Xcode 抛出编译错误:“ld : unable to insert branch island.No insertion point available. for architecture armv7”、“clang: error: linker command failed with exit code 1 (use -v to see invocation)”

That error usually means there is just too much code in single module. Typically it is caused by having lots of script code or having big external .NET assemblies included into build. And enabling script debugging might make things worse, because it adds quite few additional instructions to each function, so it is easier to hit that limit.

Enabling managed code stripping in player settings might help with this problem, especially if big external .NET assemblies are involved. But if the issue persists then the best solution is to split user script code into multiple assemblies. The easiest way to this is move some code to Plugins folder. Code at this location is put to different assembly. Also check the information about how special folder names affect script compilation:

Unity iOS 目前不支持的功能
报告 iOS 上的崩溃错误