OnDestroy 在 Unity 中可靠吗?

Ras*_*ond 4 c# unity-game-engine

在 Unity 中,我有一个脚本,我在其中通过 TCP 连接到套接字,并希望在每一帧都使用此连接。之后我需要处理和清理。我的想法是用来Start()开始连接和OnDestroy()

public class Foo : MonoBehaviour
{
    void Start()
    {
        // start TCP connection
    }
    void Update()
    {
        // use connection
    }
    void OnDestroy()
    {
        // cleanup
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要清理来执行任何发生的事情。OnDestroy()无论对象发生什么情况,该方法是否保证在应用程序停止之前(在独立模式和编辑器中)被调用?如果没有,我如何保证清理?

der*_*ugo 6

不它不是!

OnApplicationQuit当您的应用程序因某种原因崩溃时,甚至可能不会被调用。

还有其他特定情况,两者都没有被调用。我知道,根据我自己的经验,例如在 HoloLens2 上的应用程序并未关闭,而只是处于休眠状态。如果您然后通过 HoloLens 主“菜单”关闭它们,那么您实际上是通过任务管理器杀死它们。

这是非常脏,原因既不OnDestroy也不OnApplicationQuit或任何其他特定的统一消息被调用,我们结束了佐比线程和依然占据TCP端口。


如果你真的想确定(例如为了免费连接,杀死线程等)我最终做的是用解构器(Finalizer)创建一个专用类

解构器是纯 c# 并且不依赖于 Unity 的正确关闭,因此即使垃圾收集器自动完成其工作后应用程序因崩溃而终止,也能保证调用它。

public class Foo : MonoBehaviour
{
    private class FooInternal
    {
        public FooInternal()
        {
            // create TCP connection
            // start thread etc
        }

        public void Update ()
        {
            // e.g. forward the Update call in order to handle received messages
            // in the Unity main thread
        }

        public ~FooInternal()
        {
            // terminate thread, connection etc
        }
    }

    private FooInternal _internal;

    void Start()
    {
        _internal = new FooInternal ();
    }

    void Update()
    {
        _internal.Update();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你从不传递对_internal其他任何东西的引用,GC 也应该在这个实例被销毁后自动终止它。


Jas*_*n C 5

你正在寻找OnEnable建立你的联系,并OnDisable清理它。

OnDestroy(和fwiw)的问题OnApplicationQuit是,如果禁用脚本,则不会调用它,如果出现以下情况,则可能会发生这种情况:

  • 您拜访SetActive(false)GameObject父母或任何其父母。
  • enabled = false在脚本组件上设置。
  • 您可以GameObject通过取消选中检查器中的框来在播放模式下禁用该或其任何父级。
  • 您可以通过取消选中检查器中的框来禁用播放模式下的脚本组件。
  • 编辑器将在播放模式运行时重新编译脚本(对于某些类型的清理代码来说,这是一个特别令人恼火的事件[咳嗽 看着你,Vuforia ])。

问题是,如果脚本在禁用后重新Start启用,则不会调用它(它将第一次调用,但不会再调用),这在与上述列表相反的所有情况下都会发生(如以及当编辑器在播放模式下完成脚本重新编译时)。

另一方面:

  • OnDisable当脚本由于上述任何原因转换为禁用状态时,以及OnDestroy所有涉及的内容时,将被调用OnApplicationQuit
  • OnEnable当脚本由于上述任何原因以及涵盖的所有内容而转换为启用状态时,将调用Start

此外,您还将获得 Unity 的 1:1 对应OnEnableOnDisable调用(当然您可以随时自己调用它们)。

几乎Start只会有和的 1:1 对应关系OnDestroy:值得注意的例外是,如果脚本组件最初被禁用(即您在编辑模式下禁用了它),但它GameObject已启用,您仍然会OnDestroy在脚本上被调用,即使Start是没有被调用(现在我想这可能是一个错误)。另外,当然,您会错过上述所有情况。

OnEnable+OnDisable然而,干净地处理所有这些,并且通常以简单的方式完全按照您希望的方式进行操作,(理论上)没有奇怪的怪癖或陷阱。


对于问题本身,是的,OnDestroy是可靠的:当脚本被销毁时,它会按照记录被调用。

当然,如果应用程序崩溃,它不会被调用,但一般来说,您会遇到更大的问题,而这不是您通常编写代码的情况。如果你在运行游戏的机器旁边引爆手榴弹,它也可能不会被调用,但是,同样,这都属于“未定义行为”领域。