Singleton class inheritance not working

To be fair, I did kinda accidently derail and extend the OP’s original post. That’s my fault. Sorry!

I don’t see anything obviously wrong with the code, although it’s somewhat redundant.

Sure.

You’ve got the basic Awake style, which is the same pattern you used.

static Singleton instance;

void Awake () {
    if (instance) {
        Debug.LogError("An instance already exists", instance);
        Destroy (this);
    }
    instance = this;
    DontDestroyOnLoad(GameObject);
}

Then you can run with a lazy pattern

Singleton _instance;

Singleton instance {
    get {
        if (_instance) return _instance;
        GameObject clone = new GameObject ("Singleton");
        DontDestroyOnLoad (clone);
        _instance = clone.AddComponent<Singleton>();
        return _instance;
    }
}

The other patterns are variations of these two. Sometimes I don’t need a GameObject at all, so you can just use the lazy pattern with a vanilla new.

Often I don’t want true singletons, I want a new instance to replace the old one. Great to set up scene specific singletons.

Of course often Singletons are a bad idea altogether. But that’s another thread.

I tried to read and follow what this post was going on about… Is it resolved or not? :slight_smile:

fascinating how people complicateing simple things

That’s something which should have raised some attention.

According to your explanation, the SingletonScriptHost lives in the initial boot scene and the button in another scene (SceneX).
Is that correct?

So in the editor, the button in SceneX can only access the Scene X gameObjects (but there’s no way for the SingletonScriptHost to be assigned) and besides that, all the assets.

If you had another instance in SceneX, this could be wired in the editor but the reference would be missing once it calls ‘Awake’ due to its destruction and the callback wouldn’t be available anymore, so the button qouls ignore it.

Based on that, I assume you’ve accidently wired up a prefab. That’s another ‘instance’ that exists more or less inactively and that one wouldn’t run Awake or any other methods, thus sitting around and responding as a ‘valid’ subscriber.

Just a little note for the ones that might find this useful:

That implementation should be used with care.
I’d still combine this one with an Awake() approach. Otherwise you can just drag another instance into your scene or someone uses AddComponent etc…, Sure, the getter will only ever get the one that has been set up lazily, but the other instances could still mess around.

3 Likes

I was thinking the same thing about the button referencing in the wrong scene. I asked if this was ever resolved, before I went into explaining how to maybe fix that up… Anyhow :slight_smile:

Ah, this is your problem. There’s one TestSingleton on the game object in the first scene, and there is one (I’m guessing) on a prefab in your project folder that you’re dragging onto the button. So it’s not actually duplicating anything; you’re just directly referencing the one that’s sitting on the prefab rather than the one you put into a scene. Like I was saying earlier, you pretty much never want to grab a copy of the Singleton component or drag it anywhere onto any fields; the whole point of a Singleton is that you only access it through the class name and its static “Instance” field. You’re bypassing that entirely by dragging a copy onto the button and calling its methods directly. The button should have its own script with an OnClick that it calls, like this:

public class ButtonScript : MonoBehaviour {
    public void OnClick()
    {
        TestSingleton.Instance.ChangeScene("Test");
    }
}

Then there’s no need to drag a copy of the TestSingleton object anywhere; you don’t have to care where it is, you just access it through the static Instance variable.

1 Like

Cool, that’s exactly the suggestion I was waiting to make if he still hadn’t gotten it :slight_smile:

Found your issue.

You have UnityEvent instances that are referencing TestSingleton in subsequently loaded scenes.

You say that come scene 3, the id changes. Yeah… because the button in scene 3 is referencing the singleton you put in scene 3.

SURE, you destroyed said Singleton. But it was already created. And here’s the thing… the ‘Destroy’ method only destroys the unity engine side of things (the C++ object). The mono/C# object still exists in memory, and it’ll get cleaned up by garbage collection when it becomes eligible.

BUT, because your various UnityEvents (OnClick) is referencing that object, the mono/C# object isn’t getting cleaned up.

Anyways, even if it did… it would have no object to reference. Because UnityEvent is referencing the object directly, not the Singleton.Instance property.

This is why I have a ‘SingletonProxy’ that I use for such things… you tell it the type of the singleton, and it reaches out for it. So that you don’t have this overlapping issue.
https://github.com/lordofduct/spacepuppy-unity-framework/blob/master/SpacepuppyBase/SingletonProxy.cs

See, this is why I asked how YOU are implementing it, and to give an example of the problem actually happening. Just because you’re using a likeness of a common implementation, we don’t know exactly how you implemented or what bugs may exist in your specific implementation.

Oh wait… sorry, forget everything I said. It’s all just a chip on my shoulder.

Or was that chip on yours?

1 Like

That’s the first thing that comes to mind, but the event system takes care of destroyed objects even though they do technically still exist in memory in that case.

1 Like

I haven’t checked the implementation of the event system to be sure.

In general terms if you keep a reference to a MonoBehaviour it’s possible to call code on it after it’s been destroyed.

Ah yeah if there is a Singleton in every scene and you’re directly referencing one, it’s the same problem as directly referencing a prefab. Still, same solution: don’t directly reference Singletons anywhere, use Singleton.Instance.

Yup, I’m aware of that. However, the event system apparently takes these objects into account and prevents any calls to these objects even though a non-Unity-API touching method would run just fine.

I owe you a pint, that’s exactly what’s gone wrong.

1 Like

Indeed thanks. I had identified this as an option in my first reply but it seemed to get ignored by subsequent posts and things started to go around in circles. I got there in the end though.

So the problem is solved, I just have to use the second method I mentioned at the start and use script to glue the UI events to the singleton. I really wanted to avoid this sort of design, but hey ho.

Thanks again @Suddoha and everyone else that helped.
Sometimes the most obvious mistakes stare you right in the face!.

1 Like

Thanks!