Destroy() doesn't create NullReferenceExeption?

I’ve got 2 GOs in my game. One has the Controller script applied and the other has the Score script applied.

Controller

var s : Score;

function Start ()
{
	// s is now null, because i'm destroying it.
	Destroy (s.gameObject);
	
	yield WaitForSeconds (1.0);
	
	// This should throw a NullReferenceExeption.  But it doesn't!  Why!?
	s.points = 20;
}

Score

var points = 10;

The Controller destroys the score GO at the start of the game. So when I try to change the score’s points variable, I should get a NullReferenceExeption, but I don’t get any errors? Any idea why?

First, destroy does not destroy an object immediately. If you want to be rid of it instantly, use DestroyImmediate(…). That is not recommended, though.

Second, referencing a destroyed object does not generate a NullReferenceException. It destroys the object but the reference will not be set to null. It will throw a MissingReferenceException instead.

Third, upon calling destroy, it’s quite likely that the system performs a StopAllCoroutines(…), meaning the line where you expect the error is never executed.

After I set the s.points, I added a debug.log() (to see if it was executing) and it did execute. It just doesn’t generate a MissingReferenceExeption, or any other errors.

Here’s my new Controller script:

var s : Score; 

function Start () 
{ 
   // s is now null, because i'm destroying it. 
   Destroy (s.gameObject); 
    
   yield WaitForSeconds (1.0); 
    
   // This should throw a MissingReferenceExeption.  But it doesn't!  Why!? 
   s.points = 20; 
   Debug.Log (s.points);
}

Here’s an example project:

169667–6114–$missingreferenceexeption_bug_172.zip (1.07 MB)

Part of the answer you ignore that perfectly was given:

don’t forget, you work within a managed environment, there is no guarantee that anything happens immediately (that would be bad for the performance)
if you want to get a control state that ensures that “destroyed = no longer used”, then add s = null; right after the destroy line

In the Destroy() docs it says:

So destroy() should not take any longer than a frame? Correct?

Destroy docs:

I set yield WaitForSeconds(10) and it still didn’t throw a MissingReferenceExeption. How long does the destroy() function take?

Your function call happened in this frame, not in next frame.
Check it again with the next frames call to it and it will give you an exception

Also if you just set it to null after destroying (something I would do anyway as that is the regular way to flag objects for garbage collection), it will also give you exceptions

I don’t understand. Aren’t I already doing that in my script above (Controller)? I destroy the score GO and then access it a second later. This is many, many frames later. Can you clarify?

I just tried it, and you’re right. It does give me a NullReferenceExeption. Thanks!

I just don’t understand why calling Destroy() doesn’t give a MissingReferenceExeption when trying to access it 10 seconds after. Because the docs say that object destruction happens before rendering the next frame.

It runs the yield WaitForSeconds(10) on the frame it’s destroyed, but the yield is interrupted by the Destroy next frame. As such, no exception is generated.

So Destroy() can interrupt yield statements? Ok. Now I made the Score script destroy itself. The yield shouldn’t be interrupted now, right? Here are my new scripts:

Controller

var s : Score; 

function Start () 
{ 
   // s destroys itself at the start of the game.
   yield WaitForSeconds (3.0); 
   
   // This should throw a MissingReferenceExeption (because s destroyed itself earlier).
   // But it doesn't!  Why!? 
   s.points = 20; 
   Debug.Log (s.points);
}

Score

var points = 10;

function Awake ()
{
	Destroy (gameObject);
}

And Unity still doesn’t throw a MissingReferenceExeption. Why doesn’t it?

Destroying the game object does not destroy the instances of any of the attached MonoBehaviours. The garbage collector does the cleaning up of MonoBehaviour instances that were attached to destroyed game objects. The garbage collector only cleans the MonoBehaviour instance if nothing is referencing that class, but you are referencing it in your controller. Setting s to null will remove the MonoBehaviour reference and then the garbage collector will clean up that instance. (or you can call Destroy() on reference s itself to force it to be destroyed)

Just set s to null to mark it for “freeing” and to have a clear state post that point.
As I tried to explain above: You work in a managed environment, there is no guarantee of the point in time and order in which garbage correction will happen. Any kind of explicit control requires that you take that control and do it.

What it definitely will never be is “remove and expect it to be gone within the same script function” unless that loops forever.

@Shawn: Ok, now I’m specifically destroying the Score script, and I still don’t get a MissingReferenceExeption. Here’s my new Score script:

Score

var points = 10;

function Awake ()
{
	Destroy (this);
}

This didn’t change anything.

Ok, I always thought that Destroying a GO totally got rid of it, all it’s components/monobehaviours, and all of it’s children before rendering the next frame. At least, this is what I understand from reading the docs. The docs specifically say that objects always get destroyed before rendering the next frame.

that will destroy it for the engine.
But the object itself remains valid until the .NET GC cleans it.

As mentioned, use destroy and then set the reference to null, which is common behaviour and good practice.

No idea why you want to enforce bad practice, lose a lot of time on nothing and not gaining anything but problems and anomalies.

So to sum it up:

function Start ()
{
   // s is now null, because i'm destroying it.
   Destroy (s.gameObject);
   s = null; // this marks s as free to be collected by the GC in the next collection round and ensures that no other object is able to use it until then
   
   yield WaitForSeconds (1.0);
   
   // This should throw a MissingReferenceExeption.  But it doesn't!  Why!?
   s.points = 20;
   Debug.Log (s.points);
}

Daniel,

I agree with you, I downloaded your project and you can even see in the inspector that the component is missing immediately, yet, a second later it gives the correct score. It should be giving some kind of error (missing component exception) and it always has for me in the past whenever I’ve destroyed a script instance and tried to access it by the next frame.

Nathan

@Nathan: THANK YOU! That’s exactly the problem!

Perhaps if you read above it starts to make sense why it is that way.

Unity is Unity but Unity as the whole rest of your code are handled by the .NET GC, which is what handles NULL or not NULL. Unity can only remove the object from its own control and no longer access it nor update it (-> it vanishes from the editor links).

If you want it to be gone from the GC as well, null its reference or your reference counter will NEVER reach 0 thus it will never be removed.

Really what more is required till you just read what has been posted and think about why the provided source makes sense and why yours is just wrong (two people agreeing on a faulty solution doesn’t make it more correct)

No object will be freed as long as you keep a reference to it active.
Also if you want the object to be freed you have to null all references to it (or to its cycle)

This is entertaining. I wasn’t aware that objects hung around like this, although I suppose it shouldn’t really come as a surprise. It’s presumably an unavoidable consequence of having a garbage collected language.

I did some tests myself and found some additional interesting facts. For one thing, if you do this:

Destroy(something);

yield;					// Wait for Destroy to take effect

if(something == null)
	Debug.Log("Something is null");

…it will tell you that the reference is null even though it’s still possible to access its members. This at least means that a null test is a sensible way to ensure that you don’t accidentally access an object that is supposed to have been destroyed.

This is probably why I never noticed this behaviour before. As a rule, whenever I consider it possible for an object to be unexpectedly destroyed, I always do a null test before accessing its members.

Secondly, accessing any Unity API feature associated with the object will result in a MissingReferenceException:

Destroy(something);

yield;

// Each of these causes a MissingReferenceException:

var c = something.GetComponent(SomethingElse);
var g = something.gameObject;                     
var n = something.name;

However, you don’t get MissingReferenceExceptions for any members that you defined yourself.

1 Like

Thanks everyone.

And wouldn’t be a nice solution for this to have a GameObject check function like Alife(GameObject). The engine knows perfectly if an object is being destroyed or not so that would be a nice solution to avoid using null comparisons. Does this already exist on Unity?

I have a similar problem. I have a space ship being fired by 2 weapons. Each weapons have the ship as member of its classes as target so when a weapon destroys it, the other weapon needs to know that the object has been already destroyed to avoid sending messages to the ship. So if I could do something like Alife(ship) that would solve my problem.

I presume you mean “Alive”. I’m not sure how you would want it to work. If it’s a function that tests if an object has not been destroyed, then it doesn’t really help much:

if(something != null) {...}

...vs...

if(Alive(something)) {...}

On the other hand, if you want it to be an event like Update that gets called whenever your object is ‘alive’, Update already works like that. Once an object has been destroyed, events are no longer called on it.

In any case, there’s no reason to avoid null tests. They’re simple and efficient.