How to unsubscribe from a delegate that is inside of a singleton?

In my GameManager, I have a delegate. In my Door script, I subscribe to this delegate. I’ve tried unsubscribing from it in both the OnDisable and OnDestroy methods in the Door script. In both cases, I get an error when I stop running the game in the editor:

Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)

Using Debug.Log, I found out that this is because the GameManager is getting destroyed before the Door script. Even if I do a null check inside either the OnDisable or OnDestroy of the Door script to see if the GameManager is null, I get the same error

if (GameManager.Instance)
    {
        GameManager.Instance.OnAllEnemiesKilled -= OpenDoor;
    }

Somebody told me that I don’t need to unsubscribe from it, as the delegate will automatically become null when the Door object is destroyed, but that’s not true. During runtime, after the Door is destroyed, my update loop inside of the GameManager is still printing that the delegate has one subscriber: the Door.

The problem is your singleton implementation. When you stop the game the singleton object will be destroyed as well. However since you access “Instance” in OnDestroy you will recreate the manager. There are several solutions to this problem. First of all this is mainly an issue in the editor. When OnDestroy is called due to stopping playmode the environment is currently resetting / reloading the previously serialized state. When you create objects at this stage they would interfer with the scene representation during edit time.

One solution would be to avoid recreating the singleton when it got destroyed. For this you could simply add a static bool to the singleton which you set to true in the OnDestroy method of the singleton. Inside your Instance getter you only create an instance when that static variable is false. Of course that would mean you can’t rely on the Instance returning an object when used in OnDestory.

Another way is to bypass the overloaded “==” operator in your singleton implementation and do an actual reference equal check. Even when the singleton gets destroyed, the managed object is still alive but a null check would usually return true.

So instead of

if (m_Instance == null)

you could simply do

if ((object)m_Instance == null)

Note that this approach does not work if your singleton instance can actually be destroyed during runtime as that would mean it can never be recreated.

Have you tried to simply set to null the event in the OnDestroy / OnDisable of your GameManager? c# - How to remove all event handlers from an event - Stack Overflow