Destroying and recreating a singleton.

Hi,

I’m working on the save/load system for my game. I have a number of singleton manager classes based on the implementation here, for the sake of simplicity and keeping things clean I would to destroy these managers and re-instantiate them from their prefabs. I am not particularly concerned with the persistence of the managers, only that there is always only one instance.

For example, in the case of my ObjectLocationManager I am doing the following:

ObjectLocationManager objectLocationManager = FindObjectOfType<ObjectLocationManager>();
Destroy(objectLocationManager.gameObject);

GameObject objectLocationManager = Instantiate(Resources.Load<GameObject>("ObjectLocationManager"));

However, it seems by the nature of this singleton implementation that they cannot be destroyed this easily as when another class attempts to access the new instance it gives the warning:

"[Singleton] Instance '"+ typeof(T) + "' already destroyed on application quit." +  " Won't create again returning null."

Or if I disable the applicationIsQuitting sections of the code then:

"[Singleton] Using instance already created: " + _instance.gameObject.name

At the risk of displaying my ignorance, I believe this is because the instance is never really destroyed when the gameobject is destroyed as _instance is a static variable.

My question therefore, is it possible to destroy the singleton gameobject and the static instance with this singleton implementation? Or if that is not possible, what kind of singleton implementation could I use for a non-static singleton?

Thank you for any help.

Actually the implementation on the wiki is quite nonsense for several reasons:

  • locking the instance getter it completely pointless because you can’t / shouldn’t use it from any other thread anyways. Methods like “FindObjectOfType”, “new GameObject”, “AddComponent” and even setting the name of the gameobject can’t be done on any other thread than the main thread. So locking in a monobehaviour based singleton is just a waste of performance.
  • Trying to fight multiple instances it actually pointless, especially if it’s only done when the static instance hasn’t been set yet.
  • It doesn’t use DontDestroyOnLoad for singletons which are located in the scene. It only uses it when it creates an instance on the fly which is an inconsistent behaviour.
  • Finally implementing OnDestroy not as virtual makes it either impossible for the actual singleton to implement OnDestroy if it needs it, or it will break the functionality that the singleton might need.

It should be clear that there’s no way to prevent multiple instances for MonoBehaviour based singletons as you can always use AddComponent with the singleton class to create a new instance. So you shouldn’t care about that too much.

There are in general 3 different kinds of MonoBehaviour based singletons:

  • Just a usual singleton. It would create itself when it wasn’t created yet. In this case you could actually use a true singleton (not derived from monobehaviour and with a private constructor). However if you want to be able to inspect the instance for debugging purposes it might come in handy.
  • A singleton that you create manually and that you save into the scene. This are usually manager on which you want to setup important links to other objects in the scene.
  • A singleton that is based on a prefab or ScriptableObject asset in a resources folder which is mainly used to hold references to other assets in the project.

Those three kinds would have quite different implementations.

The classic singleton

First the easiest threadsafe "non monobehaviour" singleton would look like this:
public sealed class MySingleton
{
    public static readonly MySingleton Instance = new MySingleton();
    private MySingleton() { }
    // your singleton stuff here
}

A static field initializer is threadsafe. The instance will be lazily created when you first access the Instance variable. The private constructor prevents the creation of instances from outside. The class is sealed so no one can derive a class from this one. This is the perfect singleton for pretty much all cases. Of course in this case we can’t really define a reusable base class. Though it’s not necessary for those few lines.

The scene based MonoBehaviour singleton

public class SceneSingleton<T> : MonoBehaviour where T : SceneSingleton<T>
{
    private static T m_Instance = null;
    public static T Instance
    {
        get
        {
            if (m_Instance == null)
            {
                m_Instance = FindObjectOfType<T>();
                // fallback, might not be necessary.
                if (m_Instance == null)
                    m_Instance = new GameObject(typeof(T).Name).AddComponent<T>();
                DontDestroyOnLoad(m_Instance.gameObject);
            }
            return m_Instance;
        }
    }
}

This is all you will ever need if you want a manager that needs to be located and initialized in the scene. Adding an Awake method just complicates the actual implementation. FindObjectOfType isn’t that bad when it’s called once.

If the manager is required to be initialized in the scene, the fallback could be replaced with a Debug.LogError since lazy initialization won’t help much as the instance won’t be initialized (no fields assigned).

Note that you should not place such a singleton inside a scene which you intend to load multiple times. If you need this feature you should not use DontDestroyOnLoad. Just let the singleton be destroyed when the old scene get wiped out and let it automatically re-initialize when the new scene is loaded.

The prefab based singleton

public class PrefabSingleton<T> : MonoBehaviour where T : PrefabSingleton<T>
{
    private const string m_AssetPath = "Singletons/";
    private static T m_Instance = null;
    public static T Instance
    {
        get
        {
            if (m_Instance == null)
            {
                m_Instance = FindObjectOfType<T>();
                if (m_Instance == null)
                {
                    var prefab = Resources.Load<T>(m_AssetPath + typeof(T).Name);
                    if (prefab == null)
                        Debug.LogError("singleton prefab missing: " + m_AssetPath + typeof(T).Name);
                    else
                        m_Instance = Instantiate(prefab);
                }
                DontDestroyOnLoad(m_Instance.gameObject);
            }
            return m_Instance;
        }
    }
}

This basically looks almost the same as the one above, but instead of creating an instance on the fly as fallback we load them from the resources folder. It will use the constant “m_AssetPath” inside the resources folder and expects the prefab to be named exactly the same as the actual singleton class name. Of course we could again implement a lazy inizialization fallback if the prefab isn’t found, but since we explicitly want a prefab based singleton it’s better to show an error.

The ScriptableObject singleton

public class SOSingleton<T> : ScriptableObject where T : SOSingleton<T>
{
    private const string m_AssetPath = "Singletons/";
    private static T m_Instance = null;
    public static T Instance
    {
        get
        {
            if (m_Instance == null)
            {
                m_Instance = FindObjectOfType<T>();
                if (m_Instance == null)
                {
                    var m_Instance = Resources.Load<T>(m_AssetPath + typeof(T).Name);
                    if (m_Instance == null)
                        Debug.LogError("singleton asset missing: " + m_AssetPath + typeof(T).Name);
                }
            }
            return m_Instance;
        }
    }
}

This is again very simiar to the one above but uses a ScriptableObject asset as base. There isn’t any good reason to derive a singleton from ScriptableObject if you don’t want it to initialize and store it as asset. So again lazy creation makes no sense. Note that this version does not create an instance of the asset, but uses the asset directly. Keep this in mind as any changes to serialized variables will actually persist when testing in the editor. The great thing is you can modify parameters on the scriptable object asset during runtime and get immediate results while in playmode. Also when you exit playmode the changes persist.

If you want you can just use Instantiate the same was as before to actually work on an instance during playmode. Of course changes to the instance wont’ affect the asset and changes to the asset won’t be noticed until a re-enter of the playmode.


Now about destroying / reinitializing a singleton. Usually you don’t want to do this at all. But if required you can do it.

  • The true, non monobehaviour singleton can’t really be destroyed or reinitialized as the static field is read only. If you need this you have to do the usual singleton approach with a normal private static variable. To force a reinitialization you just add a method “Destroy” which sets the static reference to “null”.
  • The scene based monobehaviour singleton can’t really be “reset”. Of course you can simply destroy the instance but if it relies on an initialized instance in the scene this should only be done right before re-loading the scene.
  • The prefab based monobehaviour can simply be destroyed with this line DestroyImmediate(MySingleton.Instance.gameObject);. Since the singleton will automatically recreate itself from the prefab in case it’s reference is null we don’t have to do anything else.
  • The scriptable object singleton that uses the asset directly must not be destroyed and can’t be “reinitialized” since you directly work on the asset. If you use an instance you can simply use DestroyImmediate(MySingleton.Instance); just like with the prefab version. The singleton will reinitialize it self when accessed the next time.

Keep in mind that you should never “cache” a singleton reference locally. A singleton should always be accessed through it’s single access point (the Instance property). Of course inside a method you can use a temporary local variable to cache it temporarily when you need to use it alot in a batch task. Though in general: caching singletons is bad.