专栏首页tkokof 的技术,小趣及杂念编程小知识之 Object.Destroy

编程小知识之 Object.Destroy

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/tkokof1/article/details/102699005

本文简单描述了 Unity 中 Object.Destroy 的一些知识。

Object.Destroy 应该是 Unity 开发中最常用的函数之一了,对于该函数的一个基本认知是:

  • Object.Destroy 是异步执行的,并不会立即生效

Object.Destroy 的异步特性让下面这种销毁代码成为了可能:

for (int i = 0; i < transform.childCount; ++i) 
{
    var child = transform.GetChild(i);
    Object.Destroy(child.gameObject);
}

如果 Object.Destroy 是同步执行的话,我们就不能简单的通过递增的索引(i)来获取 transform 所有的子节点,因为 Object.Destroy 之后,子节点的索引会产生变化(递减).

有一个简单技巧可以解决同步销毁过程中子节点索引递减的问题,那就是从后往前销毁子节点,这种方式可以保证各个子节点的索引在销毁过程中不会发生变化:

for (int i = transform.childCount - 1; i >= 0; --i) 
{
    var child = transform.GetChild(i);
    Object.Destroy(child.gameObject);
}

可惜技巧大多有扩展性不高的问题,当我们需要销毁某几个子节点(而非所有子节点)的时候,这个技巧便没有什么用处了~

Object.Destroy 的异步特性还带来一些恼人的陷阱,考虑以下代码:

// codes destroy obj first
Object.Destroy(obj);

...

// before truely destroy,
// check obj and pass it to some logic if valid
if (obj) 
{
    SomeLogic(obj);
}

...

// after truely destroy,
// some logic use obj, Ops ...
obj.DoSomething();

由于 Object.Destroy 的异步特性,在调用 Object.Destroy 之后(但在真正执行销毁操作之前),销毁对象(obj)仍然是有效的,不注意这点就容易产生很多的(无效)对象访问错误.

自己来维护有效引用是规避这种陷阱的一种方法:

// codes destroy obj first
Object.Destroy(obj);
// manually set obj to null
obj = null;

...

// before truely destroy,
// check obj and pass it to some logic if valid,
// since obj is null here, we will skip pass
if (obj) 
{
    SomeLogic(obj);
}

关于 Object.Destroy 普遍还有一些类似的错误认知:

  • Object.Destroy 下一帧才会真正生效
  • Object.Destroy 过几帧之后才会真正生效
  • Object.Destroy 本帧不会生效,下一帧开始后就真正生效了

实际上, Unity 文档中已经说的很清楚:

Actual object destruction is always delayed until after the current Update loop, but will always be done before rendering.

实际的销毁操作发生于本帧的 Update 之后,结束于本帧的 渲染 之前.不过根据我的测试,实际销毁操作的窗口期要更小一些,应该至少是 发生于本帧的 Update 之后,结束于本帧的 LateUpdate 之前(当然测试结果并不足以确定问题,实际开发中还是应该按照文档的说明为主).

基于此,我们就可以明确 Object.Destroy 真正生效的时间点了:

  • 实际的销毁操作发生于本帧的 Update 之后,结束于本帧的 渲染 之前.
  • 如果在实际的销毁操作发生之前(譬如 Update 中)调用 Object.Destroy,那么实际的销毁操作就会在本帧(Update 之后,渲染之前)生效.
  • 如果在实际的销毁操作发生之后(譬如 OnGUI 中)调用 Object.Destroy,那么实际的销毁操作就会在下一帧(Update 之后,渲染之前)生效.

这里贴下 Unity 中脚本事件的流程图,可以帮助我们明确各个事件发生顺序:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 指针、引用和常量的一些“故事”

      C++也算是学了有些年头,可惜还是不甚了解,这不,今天对于指针、引用和常量这三个在C++中处处可见的东西又有些懵里懵懂了,也罢,今天就稍稍学究一下,再尽力整...

    用户2615200
  • 一种稀疏矩阵的实现方法

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tkokof1/article/details/828...

    用户2615200
  • 编程小知识之 JavaScript 数组拷贝

    Array.map 可以对数组元素进行映射(map)操作,如果提供一个自身到自身的映射函数,我们便可以实现数组的拷贝了.

    用户2615200
  • 目前可以使用ES10的5个新特性

    ECMAScript 2015,也称为ES6,是一个花了6年时间完成的主要版本。从那时起,负责ECMAScript标准开发的技术委员会39 (TC39)每年都会...

    前端小智@大迁世界
  • 移花接木:当泛型方法遇上抽象类----我的“内存数据库”诞生记

    之前,不怕“重复发明轮子”的我,搞了一个“PDF.NET框架”,即“PWMIS数据开发框架”(目前已经开源),自己用特殊的方式设计了一个实体类基类,然后又设计了...

    用户1177503
  • Seurat包的findmarkers函数只能根据划分好的亚群进行差异分析吗

    虽然是免费的, 但是学员关于课程的提问,我还是会尽我所能帮忙的。当然,受限于时间和精力,只能是挑选重点和普适性的有价值的问题,比如其中一个问题是:

    生信技能树jimmy
  • 可用于实时实例分割的Deep Snake算法(CS CV)

    本文介绍了一种基于轮廓的实时实例分割方法——Deep Snake 算法。与最新的一些直接将物体边界点的坐标从图像中返回的方法不同,Deep Snake使用神经网...

    DANDAN用户6837186
  • AtomicInteger 源码分析

    AtomicInteger 扩展了 Number,适用于基于数字的处理,并提供了如原子递增等,适合一些计数场景

    itliusir
  • 挑战程序竞赛系列(16):3.1最大化最小值

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    用户1147447
  • ubuntu蓝牙音响配对成功但在声音设置中无法设置 解决

    zhangrelay

扫码关注云+社区

领取腾讯云代金券