UnityEngine.Object.Destory 和 NGUITools.Destroy 方法之间的区别

我们先来看看 NGUITools.Destroy 方法:

static public void Destroy (UnityEngine.Object obj)
{
    if (obj != null)
    {
        if (Application.isPlaying)
        {
            if (obj is GameObject)
            {
                GameObject go = obj as GameObject;
                go.transform.parent = null;
            }

            UnityEngine.Object.Destroy(obj);
        }
        else UnityEngine.Object.DestroyImmediate(obj);
    }
}

我们可以看到这个方法最终也是调用的 Unity3D 提供的 UnityEngine.Object.Destroy 方法,区别一是根据当前游戏是否在运行,然后选择调用 Destroy 还是 DestroyImmediate 方法,区别二是在调用 Destroy 的逻辑中加入了将销毁对象的 Transform 的 parent 设置为 null 了,这个小细节有什么作用呢?NGUI 这么一个成熟的插件为什么在这个地方会做这样的处理呢?之前我并不知晓其中的缘由,直到今天我碰到了一个跟 Transform.childCount 相关的问题,我才懂了。接下来我们来看一个栗子,这段代码中,我们要做的就是从一个父控件中移除子控件,直到父控件中子控件的个数与目标数量一致。

void DestroyChildenToCount (Transform parentTransform, int destChildCount)
{
    if (parentTransform == null)
        return;
    if (parentTransform.childCount <= destChildCount)
        return;
    while (parentTransform.childCount > destChildCount) {
        UnityEngine.Object.Destroy (parentTransform.GetChild (destChildCount));
    }
}

上面的这段代码,我们有看出什么问题来吗?我是没有看出来,所以我就运行了这段代码,然后我的 Unity3D 就把电脑的 CPU 全给吃掉了,Unity3D 就无法正常响应操作了然后就崩溃了。通常我们这个时候的第一反应就是死循环了对吧,那么问题出在哪儿,这段代码里头就一个 While 循环,那么肯定就是这个循环出问题了,对吧。我们再来看看这段代码可能出问题的地方,我们以为只要持续的每次删掉索引为 destChildCount 的子控件,直到父控件的所有子控件的数目与目标数一致之后退出循环,达到我们预期目的,但是事实上这个 UnityEngine.Object.Destroy 方法的执行并不是及时的,这就导致了 While 循环一直无法退出的问题。我们来看看 Unity3D 官方文档上对 Destroy 方法的描述:

Removes a gameobject, component or asset.

The object obj will be destroyed now or if a time is specified t seconds from now. If obj is a Component it will remove the component from the GameObject and destroy it. If obj is a GameObject it will destroy the GameObject, all its components and all transform children of the GameObject. Actual object destruction is always delayed until after the current Update loop, but will always be done before rendering.

红色字体部分明确说明了 Destroy 的操作会在本次刷新的 Update 循环之后执行,但是会在整体渲染之前完成,这就意味着这个里头还是存在时间差的,例如每帧 0.033333 秒执行时间,有这么多的时间留给 While 循环,它不给你整出事儿来都很难啊,对吧。

所以现在我们就能理解 NGUITools.Destroy 方法中在 Destroy 对象之前将其 Transform 的 parent 指向了 null 的意图了,这样就可以及时地将被销毁对象从其原本的父对象节点上移除了,这样我们的 While 循环就可以及时的退出了。如果你跟我一样有类似的销毁某个对象的指定数目子控件的需求的话,我想这会是一个蛮有意思的知识点的。

UnityEngine.Object.Destory 和 NGUITools.Destroy 方法之间的区别》有一个想法

  1. Pingback引用通告: 由于误用 NGUITools.Destroy 导致所有 UI 控件的层都被重置的问题 | 7dot9 Laputa

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据