Unity中实例化对象销毁的分析

项目中在检查物体生命周期和进行资源对象和实例化对象回收的时候, 遇到了两个问题:

  1. 组件在OnDestroy方法中, 调用了Destroy(gameObject)方法, 会不会出问题?
  2. 组件在OnDestroy方法中, 能否取得到子物体的数据, 还是直接报空?

这两个问题, 可以转换为以下几个可以通过简单代码验证的问题

  1. 具有层级关系和同级关系的组件OnDestroy调用顺序是怎样?
  2. Unity对Destroy和DestroyImmediate的操作有没有考虑到重复调用的问题?
  3. Destroy一个物体, 会不会马上丢失了其父物体或子物体的引用?

为了验证这几个问题, 我在场景里面创建了几个有层级关系和同级关系的物体, 每个物体都添加了测试脚本TestDestroy

关系如下图

Unity中实例化对象销毁的分析_第1张图片

OnDestroy测试代码如下

public class TestDestroy : MonoBehaviour
{
    public int Depth;
    public bool DestroyChild;
    private void OnDestroy()
    {
        Destroy(gameObject);
        Log.Error("{0} Destroy again depth: {1}", gameObject.name, Depth);
        if (transform.childCount != 0)//叶子节点无法访问子节点
        {
            Log.Error("{1} Get first child depth: {0}", transform.GetChild(0).gameObject.GetComponent().Depth, gameObject.name);

        }

        if (Depth != 1)//根节点无法访问父节点
        {
            Log.Error("{1} Get parent depth: {0}", transform.parent.gameObject.GetComponent().Depth, gameObject.name);
        }
    }

    private void Update()
    {
        if (DestroyChild)
        {
            if (transform.childCount != 0)
            {
                Destroy(transform.GetChild(0).gameObject);
            }

            DestroyChild = false;
        }
    }
}

运行之后勾选物体1的Destroy Child, 销毁物体2.1, Log输出如下(定义为Error而已)

Unity中实例化对象销毁的分析_第2张图片

可以得出结论:

  • Destroy(gameObject)是安全的, 没有任何来自Unity内部的Error,
  • 具有层级关系和同级关系的组件调用OnDestroy顺序是: 层级关系按照在Hierarchy视图内的层级顺序自顶向下, 同级关系时按照在Inspector视图的顺序自顶向下
  • Destroy一个物体时, 仍然保留着对其父物体和子物体的引用, 也就是没有立即解除引用关系

注意到实例化对象的销毁有两个可以使用的接口Object.Destroy和Object.DestroyImmediate, 这两个函数有什么区别呢?

Unity的API说明文档中是这么解释的:

Object.Destroy

public static void Destroy(Object obj, float t = 0.0F);

Parameters

obj The object to destroy.
t The optional amount of time to delay before destroying the object.

Description

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.

Object.DestroyImmediate

public static void DestroyImmediate(Object obj, bool allowDestroyingAssets = false);

Parameters

obj Object to be destroyed.
allowDestroyingAssets Set to true to allow assets to be destroyed.

Description

Destroys the object obj immediately. You are strongly recommended to use Destroy instead.

This function should only be used when writing editor code since the delayed destruction will never be invoked in edit mode. In game code you should use Object.Destroy instead. Destroy is always delayed (but executed within the same frame). Use this function with care since it can destroy assets permanently! Also note that you should never iterate through arrays and destroy the elements you are iterating over. This will cause serious problems (as a general programming practice, not just in Unity).

Unity引擎资源管理代码分析 ( 2 )文章中, 对Unity的资源管理解析得很透彻, 接下来直接提取部分结论,

  • Destroy是在当帧的Update操作执行完毕后再延迟删除对象,而DestroyImmediate是在调用时立即删除对象
  • DestroyImmediate的函数说明中特别强调了只在编辑器的代码中调用它,游戏中应使用Destroy
  • 编辑器代码中调用Object.Destroy是不会作任何处理的
  • Object.Destroy调用的引擎内部函数是DestroyObjectFromScripting , 该函数进行对传入对象的检查之后, 将延迟销毁对象的回调函数DelayedDestroyCallback注册到了一个叫DelayedCallManager的类中,该类负责在每帧的Update后统一执行这些回调
  • DelayedDestroyCallback函数的实现是简单调用了DestroyObjectHighLevel这个函数
  • Object.DestroyImmediate调用的引擎内部函数是DestroyObjectFromScriptingImmediate, 该函数开头对传入的对象和allowDestroyingAssets参数进行检查之后, 没通过DelayedCallManager注册回调, 而是立即调用了DestroyObjectHighLevel函数
  • DestroyObjectHighLevel函数的HighLevel体现在哪里呢?
    • 它其实是一个递归的对象销毁函数,也就说当我们把根级GameObject传进去的时候,它会自动把其下挂接的所有子节点和组件都删除掉。
    • 除此之外它还会做一些安全处理,例如是否重复销毁,对象是否还在被物理引擎使用中等等。
  • 在游戏运行时的代码中,我们只能使用Object.Destroy来销毁通过Object.Instantiate函数实例化的对象, 它不能卸载Resources.Load加载的对象

你可能感兴趣的:(Unity,3D,Unity性能优化)