Search Unity

Null ref when subscribing to event.

Discussion in 'Scripting' started by Flynn_Prime, Oct 24, 2019.

  1. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Not sure why I'm getting an error really. My other events are setup very similarly and they work correctly.

    Code (CSharp):
    1. public class SomeClass : MonoBehaviour
    2. {
    3.     public delegate void SomeEvent();
    4.     public SomeEvent someEvent;
    5. }
    Code (CSharp):
    1. private void OnEnable ()
    2. {
    3.     SomeClass.instance.someEvent += SomeMethod;
    4. }
    Should be working as expected right?

    (Above code just typed quickly on phone, I know my singleton isn't correct)
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Is SomeClass.instance being assigned? And if so, is it being assigned before OnEnable is called? Put Debug.Log's on both to see which order they're being called in.
     
  3. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    I would suggest you add some debugs to see what is null. Are you sure instance isn't null for example?

    haha... @StarManta had the same idea, but beat me to it.
     
  4. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Seems you are both correct. The instance is null just before the subscription to the event. Not sure why as it gets assigned in the Awake method, which I thought is called before OnEnable.

    Code (CSharp):
    1. public class SomeClass : MonoBehaviour
    2. {
    3.     public static instance;
    4.  
    5.     private void Awake()
    6.     {
    7.         if (instance != null && instance != this)
    8.         {
    9.             Destroy(gameObject);
    10.        }
    11.         instance = this;
    12.     }
    13. }
    Am I missing something here? Using the debugger, the subscription code is reached before the instance is assigned. But I am baffled as to why.
     
  5. nilsdr

    nilsdr

    Joined:
    Oct 24, 2017
    Posts:
    374
    One scripts onenable can be called before another scripts awake. I think the order is only gauranteed on a per script basis.

    Code (CSharp):
    1. Note that for objects added to the scene, the Awake and OnEnable functions for all scripts
    2. will be called before Start, Update, etc are called for any of them. Naturally, this cannot be enforced when an object is instantiated during gameplay.
     
  6. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Wow I was not aware of that. So how do people normally manage events in this way? Even the examples of event manager classes I've come across use a singleton design. So how can I assure I don't keep getting this same problem?

    EDIT: I could do a null check OnEnable, and then also subscribe in Start() but this seems a bit hackish... Or perhaps this is the norm?
     
    Last edited: Oct 24, 2019
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You can force scripts' execution order so that your singleton's gets called first.

    You can implement your singleton using an accessor; if it hasn't been assigned yet, it uses FindObjectOfType to assign itself:
    Code (csharp):
    1. public class SomeClass : MonoBehaviour {
    2. public static SomeClass instance {
    3. get {
    4. if (_instance == null) _instance = FindObjectOfType<SomeClass>();
    5. return _instance;
    6. }
    7. }
    8. private static SomeClass _instance;
     
    m-wake likes this.
  8. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Thanks for the reply. Always tended to stay away from script execution order but I guess I need to use it in this case.

    As for the second part of your reply; is there any real advantage of what you suggest over my above implementation of a singleton? I don't see how finding the object by type is quicker/more advantageous of saying:
    instance = this;

    Or is it just to ensure references remain intact at run time when something tries to access the instance?
     
  9. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    The advantage is that the situation that triggered this thread is impossible with a FindObjectOfType-based singleton. It doesn't matter when you try to access the instance, it will always be able to find it.
     
  10. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Ahhh I see. Perfect. Thanks a bunch
     
  11. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I typically make my singletons assign their static instance variable in their constructor, which runs before all Unity magic methods.

    However, this doesn't let you do the normal detect-duplicates-and-destroy thing (for several reasons), so you have to ensure that you really only have one. I just have a one-line constructor saying
    instance = this
    .