Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is OnDestroy reliable in Unity?

Tags:

c#

unity3d

In Unity I have a script in which I connect to a socket via TCP and want to use this connection every frame. I need to dispose and clean up after that. My idea was to use Start() to start a connection and OnDestroy()

public class Foo : MonoBehaviour
{
    void Start()
    {
        // start TCP connection
    }
    void Update()
    {
        // use connection
    }
    void OnDestroy()
    {
        // cleanup
    }
}

I need the cleanup to execute whatever happens. Does the OnDestroy() method guarantee to be called before the application stops (in standalone mode and in editor) no matter what happens to the object? If not, how can I guarantee the cleanup?

like image 268
Rasmond Avatar asked Mar 01 '23 20:03

Rasmond


1 Answers

No it is not!

Even OnApplicationQuit might not get called when your app e.g. crashes for some reason.

And there are other specific cases where neither is called. I know that from my own experience that e.g. on the HoloLens2 apps are not closed but only hibernated. If you then close them via the HoloLens home "menu" then you actually kill them via task manager.

This is pretty dirty and causes neither OnDestroy nor OnApplicationQuit or any other Unity specific messages to be called and we ended up with zomby threads and still occupied TCP ports.


If you really want to go sure (e.g. for giving free the connection, killing threads etc) what I finally did was creating a dedicated class with deconstructor (Finalizer)

The deconstructor is pure c# and does not rely on Unity being shutdown correctly so it is guaranteed to be called even if the app was terminated due to a crash as soon as the Garbage Collector automatically does its job.

A MonoBehaviour itself shouldn't implement any constructor nor destructor but a private "normal" class can:

public class Foo : MonoBehaviour
{
    private class FooInternal
    {
        private bool disposed; 

        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()
        {
            Dispose();
        }

        public void Dispose()
        {
            if(disposed) return;

            disposed = true; 

            // terminate thread, connection etc
        }
    }

    private FooInternal _internal;

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

    void Update()
    {
        _internal.Update();
    }

    void OnDestroy ()
    {
        _internal.Dispose();
    }
}

if you never pass on the reference to _internal to anything else, the GC should automatically kill it after this instance has been destroyed as well.

like image 187
derHugo Avatar answered Mar 09 '23 06:03

derHugo