Does Destroy remove all instances?

I want to know if Destroy(obj) removes all references to that object (garbage collection permitted).

I have a Manager class that keeps a List[GameObjects] (note: [ = < ) which each of those objects holds a toBeRemoved:bool so on the Manager’s Update() the GameObjects can all be removed at once.

public void Update(){
    ...
    foreach(GameObject obj in entityList){
        if(obj.getComponent<EntityScript>().toBeRemoved){
            Destroy(obj);
        }
    }
    ...
}
  • do I need to do additional bookkeeping to remove references to this object from the list I am stepping through?
  • will this cause a segmentation/fragmentation of the list?
  • if a reference to the object is held on other scripts will those references also be removed, or do I need to do bookkeeping to remove those manually?

You have to understand how Unity works behind the scenes. Mono / .NET is a garbage collected language and it works exactly the same in Unity. In most managed languages / environments there is no manual way to destroy objects. Objects get destroyed by the GC when you “clear” all references to the object.

However a lot objects in Unity, to be more precisely all that are inherited from UnityEngine.Object, consists of two parts. A managed object and a native code c++ class in the engine itself. In the managed environment you only “see” the managed object that is “linked” with the native part. This is where two totally different worlds collide.

When you call destroy on the managed object, Unity will destroy the native code part of the object immediately (well DestroyImmediate will since Destroy is delayed one frame). The managed part of the class is still there! Unity will flag the class internally as “destroyed”. Unity has implemented a custom == operator and Equals function. In those functions unity will pretend the reference is null even when it isn’t. This makes it basically impossible to do anything with the reference at all once it’s marked as destroyed.

The managed part will be destroyed by the GC when there is no reference to the object left.

You can simply test this with the following two classes:

// Test.cs
using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour
{
    private static int m_UniqueCounter = 0;
    private int m_MyID = m_UniqueCounter++;
    ~Test()
    {
        Debug.Log("Test destroyed (" + m_MyID + ")" );
    }
    void Awake()
    {
        Debug.Log("Test created (" + m_MyID + ")");
    }
}

And this one:

// TestManager.cs
using UnityEngine;
using System.Collections;

public class TestManager : MonoBehaviour
{
    private Test otherScript;
	private Test secondRef;
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.C))
		{
            otherScript = (new GameObject("TestObject")).AddComponent<Test>();
		}
        else if (Input.GetKeyDown(KeyCode.D))
		{
            Destroy(otherScript.gameObject);
		}
        else if (Input.GetKeyDown(KeyCode.N))
		{
            otherScript = null;
		}
        else if (Input.GetKeyDown(KeyCode.S))
		{
			secondRef = otherScript;
		}
        else if (Input.GetKeyDown(KeyCode.M))
		{
			secondRef = null;
		}
    }
	void OnGUI()
	{
		GUILayout.BeginVertical();
		GUILayout.Label("C : otherScript = 'new Test'");
		GUILayout.Label("D : Destroy(otherScript.gameObject);");
		GUILayout.Label("N : otherScript = null;");
		GUILayout.Label("S : secondRef = otherScript;");
		GUILayout.Label("M : secondRef = null;");
		GUILayout.Label("---");
		GUILayout.Label("Is otherScript == null? " + (otherScript == null));
		GUILayout.Label("Is secondRef == null? " + (secondRef == null));
		
		GUILayout.EndVertical();
	}
}

Just add the TestManager to an GameObject and start the game.

  • If you press “C” you will see that a Test script has been created on a new GameObject
  • If you press now “D” the GameObject and the Test script will be removed but the destructor of Test will not be executed.
  • If you press now “N” you will see that the destructor will get executed after a short time (when the GC collects it)
  • Now press again “C” and then “D”. Now we have again a ghost reference which seems to be null but it can even be copied.
  • Press now “S” to copy the reference.
  • Press “N” to wipe out first reference → nothing will happen
  • Press “M” to wipe the second reference → the object will be collected

This “pretending null” behaviour allows a strange looking piece of code which will actually have an effect:

    if (otherScript == null)
        otherScript = null;

Usually this makes no sense but in the case of Unity it will effectively wipe the reference.

To answer your question about storing referenced in Lists or Array:

The values stored in an array or List behave the same as any other variable. Nobody will remove anything from an array (which isn’t even possible) or a List. The reference will remain the same but will evaluate to “null” once you used Destroy on the object.

No, Destroy only removes the instance it is called on.

That said, instead of iterating through the entire list each update you could move objects that need to be destroyed into a separate list and only iterate through the list if it has length - not that what you’re doing now is wrong, but if you have a large amount of objects to be managed you would see slight performance improvement.

EDIT: Sorry, I read your question quickly and thought you said instances, not references. My answer is accidentally irrelevant.

It seems Destroy() actually destroys all references of the object since this:

GameObject obj;
GameObject objI;
GameObject objII;
void Start()
{
    obj = GameObject.Find("Cube");
    objI = GameObject.Find("Cube");
    objII = obj;
}

void Update(){
    if (Input.GetKeyDown(KeyCode.Space)) {
        Destroy(obj);
    }
    if (Input.GetKeyDown(KeyCode.A))
    {
        print("obj "+ obj);
        print("objI " + objI);
        print("objII " + objII);
    }
}

prints null for all objects. So even though obj is destroyed, objI and objII are null. As a result, the actual object is good for GC. Not the reference though as they are on the stack and will get destroyed at the end of the program.

Edit: I just noticed the other question.

So any reference gets null. If you have a reference on another script on another object, it will be null as well. Destroy just cut all bridges to the stack.

For defragmentation, lists are arrays that get automatically reallocated by the compiler. So, you do not need to bother about that part.

Destroy just sets a flag on the gameObject. From now on, Unity will treat it as if that object was null, and it’ll be cleaned up when it can.

Your arrays containing that object will now (essentially) contain null references there instead.

Nothing ever removes itself from arrays, you always have to do that yourself (because if the engine does it then you’ve lost some control of your program, and that’s bad)