【Unity】DestroyされたGameObjectはnullなのか

  • 13
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

【Unity】DestroyされたGameObjectはnullなのか

UnityのGameObjectはnew GameObject();するだけでヒエラルキー上に配置され、いらなくなったらGameObject.Destroy();するだけで消える。とても便利ですね。

ところで、Destroyされてヒエラルキーからいなくなった後のGameObjectはどうしているんでしょうか?

※実行環境:Unity5.3.0f4

GameObject.Destroyしてみる

ともかくDestroyしてみます。
※実験のためDestroyImmediateを使っています。

using UnityEngine;
using System.Collections;

public class Script : MonoBehaviour {

    void Start ()
    {
        GameObject obj = GameObject.Find("GameObject");
        Debug.Log("obj is " + obj);

        Debug.Log("----Destroy----");
        GameObject.DestroyImmediate(obj);

        Debug.Log("obj is " + obj);
    }
}

obj is GameObject (UnityEngine.GameObject)
----Destroy----
obj is null

なるほど、DestroyされたGameObjectは死んでnullになっているみたいです。
これならDestroy後のオブジェクトをうっかり操作することもなし、安心便利ですね。

本当にnull?

でもGameObjectと言っても普通のクラスインスタンスのはず、Destroyしたらnullに変化するなんてことあるんですかね。

GameObject.DestroyImmediate(obj);
Debug.Log(obj == null);
Debug.Log(obj.ToString());

True
null

やっぱりnullみたいですね。

・・・。
なんかおかしいですね。

GameObject obj = null;
Debug.Log(obj == null);
Debug.Log(obj.ToString());

True
NullReferenceException: Object reference not set to an instance of an object

Destroy後のGameObjectはnullではない

GameObject.DestroyImmediate(obj);
Debug.Log(obj == null);
Debug.Log(obj.GetType());

True
UnityEngine.GameObject

やっぱりGameObjectじゃないか。
Destroy後のGameObjectは"自称null"のようです。

あくまで自称、やっぱりnullではないので、うっかりnullのつもりで触っていると思いがけない挙動をすることもあります。

??演算子

GameObject.DestroyImmediate(obj);
obj = obj ?? new GameObject();
obj.name = "new obj";

MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.

あれ? objには新しいGameObjectが入っているはず・・・
ですが、==や!=をオーバーライドしてnullを自称しているだけ(と思われる)なので、??演算子はnullだとはみなしてくれません。

GameObject.DestroyImmediate(obj);
if(obj == null) {
  obj = new GameObject();
  obj.name = "new obj";
}

obj==null の形なら問題無く。

GameObjectやComponentはメモリ上で生きている

Destroyされた後もUnityのオブジェクト管理から外れただけで、そのGameObjectへの参照がある限りメモリ上に生き続けています。
このため以下のようなコードがしれっと動いてしまいます。

using UnityEngine;
using System.Collections;

public class TestBehaviour : MonoBehaviour
{
    public int n { set; get; }

    public void Update()
    {
        Debug.Log("Update!");
    }
}

public class Script : MonoBehaviour {

    void Start ()
    {
        GameObject obj = GameObject.Find("GameObject");
        var testBehavior = obj.AddComponent<TestBehaviour>();
        var button = GameObject.FindObjectOfType<UnityEngine.UI.Button>();
        button.onClick.AddListener(() => {
            testBehavior.n += 1;
            Debug.Log("Click " + testBehavior.n);
        });

        Debug.Log("obj is " + obj);

        Debug.Log("----Destroy----");
        GameObject.Destroy(obj);
    }
}

obj is GameObject (UnityEngine.GameObject)
----Destroy----
Click 1
Click 2
Click 3

ボタンを押すたび"Click 1"、"Click 2"...と増えていきます。
GameObjectを触った時とは違い、MissingReferenceException等の例外やエラーは出ません。
しかし、objひいてはtestBehaviorはUnityからは見放されているので、"Update!"が表示されることはありません。

終わり

Destroy後は後始末して、本当のnullにするのが良いですね。

GameObject.Destroy(obj);
obj = null;
Debug.Log("obj is " + obj);

obj is