熟悉了cocos语法的都知道cc.isValid这个api可以判断节点/组件是否可用(是否已被销毁)

而Unity中却没有这样的API,往往使用==null来进行判断

那么==null到底如何来判断呢?

 

Part 1

        public int numTest = 998;
        public void Print()
        {
            Debug.Log("TestScriptOfBilly.Print this.gameObject.name " + this.gameObject.name);  //0
            Destroy(gameObject);
            Debug.Log("TestScriptOfBilly this == null 1st " + (this == null).ToString());       //1
            Debug.Log("TestScriptOfBilly this.numTest 1st " + numTest);                         //2
            Debug.Log("TestScriptOfBilly this.gameObject.name 1st " + this.gameObject.name);    //3
            DOVirtual.DelayedCall(1.0f, () =>
            {
                Debug.Log("TestScriptOfBilly DelayedCall callback this == null 2nd " + (this == null).ToString());  //4
                Debug.Log("TestScriptOfBilly DelayedCall callback this.numTest 2nd " + numTest);                    //5
                Debug.Log("TestScriptOfBilly DelayedCall callback this.gameObject.name 2nd" + this.gameObject.name);//6
            });
        }

 

结合log可以发现

Destroy之后当前帧内this(MonoBehaviour)与gameObject都未被立即销毁

1,2,3三句log可以佐证

而在延迟回调里面(Destroy之后的帧)

4,5,6三句log十分"诡异"

第4句this==null已经变为了true

第5句却仍然能访问到this.numTest的值998

第6句访问gameObject直接报错

 

这里大胆推测,this其实并不是null

而是由于

UnityEngine.Object 

public static bool operator ==(Object x, Object y);

这里重写了运算符,把已经销毁的Object根据销毁标记在与null判定相等时返回了true

 

Part 2

为此我们再做一个实验

        void testValid()
        {
            TestScriptOfBilly tsob = imgTest.GetComponent<TestScriptOfBilly>();
            Destroy(imgTest);
            DOVirtual.DelayedCall(1.0f, () =>
            {
                Debug.Log("DelayedCall callback tsob == null " + (tsob == null).ToString());
                Debug.Log("DelayedCall callback tsob.numTest 1st " + tsob.numTest);
                tsob = null;
                Debug.Log("DelayedCall callback tsob.numTest 2nd " + tsob.numTest);
            });
        }

 

可以清晰的看到

同样判空==true

1st输出了998

tsob=null赋值之后2nd未输出直接报错

因为索引null的属性报错是必然的

 

Part 3

再测试一下DestroyImmediate

        void testValid()
        {
            TestScriptOfBilly tsob = imgTest.GetComponent<TestScriptOfBilly>();
            DestroyImmediate(imgTest);
            Debug.Log("testValid imgTest == null " + (imgTest == null).ToString());
            Debug.Log("testValid tsob == null " + (tsob == null).ToString());
            Debug.Log("testValid tsob.numTest " + tsob.numTest);
        }

 

结果与预期一致 DestroyImmediate使判空可以立即生效不必等到下一帧

其余逻辑与Destroy一致

 

Part 4

最后终极测试一下,通过两个文件来操作

TestScriptOfBilly.cs
        public void DoDestroy()
        {
            Debug.Log("TestScriptOfBilly.Print and DestroyImmediate");
            DestroyImmediate(gameObject);
        }
TestSceneOfBilly.cs
        void testValid()
        {
            TestScriptOfBilly tsob = imgTest.GetComponent<TestScriptOfBilly>();
            tsob.DoDestroy();
            Debug.Log("testValid imgTest == null " + (imgTest == null).ToString());
            Debug.Log("testValid tsob == null " + (tsob == null).ToString());
            Debug.Log("testValid tsob.numTest " + tsob.numTest);
        }

结果与之前一致

侧面说明Destroy并未在销毁后对参数赋值为null

无论tsob还是前面的this都是在==操作符重载里根据内部状态来判定的