- Home /
Unsubscribe events in OnDisable causes NullReferenceException
My approach is quite simple. GameManager got static references to my sub systems like AudioManager, InterfaceManager, SavegameManager etc (managers are not destroyed between scenes) . Components on scene may subscribe to this managers events, like in this sample with ItemsManager event:
private void OnEnable()
{
GameManager.Items.OnInventoryItemChanged += OnAmountChange;
}
private void OnDisable()
{
GameManager.Items.OnInventoryItemChanged -= OnAmountChange;
}
This static properties cached on GameManager Awake (and GM is first in script execution order) and if variable is null - try to find it on scene (to find reference if I'll need this manager in Editor script, for instance.
This approach causes a bunch of NullReferenceExceptions when I stop the game in the editor. Apparently it happens because some of my managers destroyed before this OnDisable called.
By the way, all my managers is placed on the same Object:
Maybe there is a way to force this object to be destroyed at the end, when all other objects on scene is destroyed? Will script execution order affect destroy order?
In your example, what is null - Game$$anonymous$$anager or Items?
Items. I receive this null exception time to time from different systems linked with Game$$anonymous$$anager, but newer from Game$$anonymous$$anager itself. $$anonymous$$ay be it's just random luck and one day Game$$anonymous$$anager will be destroyed first
Try to unsubscribe in OnDestroy ins$$anonymous$$d of OnDisable...
You can have your script run last using the Script Execution Order settings. Not completely sure if that will solve your issue.
Answer by Deadcow_ · Jan 06, 2016 at 09:20 AM
Okay, I find workaround for this specific issue:
private void OnApplicationQuit()
{
MonoBehaviour[] scripts = FindObjectsOfType<MonoBehaviour>();
foreach (MonoBehaviour script in scripts)
script.enabled = false;
}
When OnDisable called - nothing is destroyed yet.
Answer by LordDarkon76 · Jan 05, 2016 at 09:21 PM
Sometimes the manager is destroyed before listener.
if(GameManager.Items != null)
{
GameManager.Items.OnInventoryItemChanged -= OnAmountChange;
}
Adding that protection will solve your problem.
An object reference doesn't automatically get set to null when the object is destroyed.
You made my doubt, after checking my project I use.
if(Game$$anonymous$$anager.Items)
{
Game$$anonymous$$anager.Items.OnInventoryItemChanged -= OnAmountChange;
}
Well, for UnityEngine.Object references it does thanks to Unity's fake null objects. It's actually a nice feature. You can even check if a script itself has been destroyed like this:
if (this == null)
This would be complete nonsense in normal C#, but with UnityEngine.Object references it's possible. An object becomes fake null when it has been destroyed. The managed part lives on until all references are gone. Until then you can still use the instance to some degree but a null check will return true.
This looks like a dirty way to solve this problem. I want to ensure, that my managers are always available, even if not in playmode.
Game$$anonymous$$anager check for null itself, and if Items is null and not in scene = throw custom exception "Items$$anonymous$$anager not found on scene", so in my case any call of Items will cause exception, even "Game$$anonymous$$anager.Items != null"
Your answer
Follow this Question
Related Questions
Make prefab that generated more than once listener in Unity Events 0 Answers
Check if a listener has already been added to a button? 2 Answers
One listener for multiple objects? 2 Answers
Setting up EventTrigger programmatically for GameObject throws NullReferenceException 0 Answers
Unity accepts default parameter in event subscription but fails 1 Answer