unity是如何主动释放对象的?

时间:2024-03-30 08:47:34

有一个现象是大家在使用unity时天天遇到的,也许大家对此习以为常,但其实这并不简单:

在一个脚本里引用了另一个对象(GameObject、脚本等),然后在某种情况下Destroy了这个引用的对象,然后。。。。这个引用的对象变成null了!

可能大家第一反应是,没错啊!我摧毁了这个对象,引用是不存在了啊?unity是如何主动释放对象的?

请大家再仔细想想:

我们用的语言是C#,书上怎么说?C#是托管语言,对象的回收是在对象没有任何引用以后自动执行的。

很显然,unity里的这一现象看起来违反了C#语言的定义。因为我只是执行了Destroy,并没有主动把引用置为null,对象怎么能被回收?就算你说Destroy内部主动GC了,也是说不通的。

所以,这里面其实隐藏了一些黑科技。。unity是如何主动释放对象的?

首先说UnityEngine.Object,这个是Unity引擎这个大框架的基类,所有的类都是继承自它的,因此,我们只需要对它进行分析就可以。

大家知道Unity的底层是c/c++的,所以,一般来说Unity中的一个对象其实是分为两个部分的,原生部分和C#部分。比较重要和比较需要性能的东西都是由原生代码写的,在早期的代码中原生占比很高,C#只是封了一系列的接口。C#层持有一个对原生部分数据的指针,这个看Object的C#部分代码就可以得知:

unity是如何主动释放对象的?

而原生部分的c/c++,是可以做到主动释放动态分配的内存的。因此,一个Object对象会有三种状态:

1、存在(C#部分和原生部分都存在),正常状态。

2、null(C#部分存在,原生部分被释放),这里就是调用了Destroy之后的状态

3、null(C#部分被回收,原生部分被释放),调用了Destroy并主动把C#层对象置为null会进入这种状态

直接把C#Unity对象置null不会导致C#部分引用为0被回收,因为场景树会有引用,也就是大家看到的场景中的GameObject或它上面的组件。

对C#层来说,只有第三种状态才是真正意义上的对象被释放,但其实大多数时候我们的对象是处于第二种状态。

那么,为什么我们觉得对象被释放了呢?

有哲学家说过,人永远无法真正的认识世界。

我们怎么去认识世界?通过眼睛去看,通过耳朵去听,通过皮肤去感触,通过舌头去品尝。这些传感器的有机结合告诉我们处于怎样的一个世界。

如果某些器官不正常,那我们感受到的世界就不一样了。

如果没有这些呢?(那可是比人棍还惨。。)那我们连一个虚幻的世界都感觉不到

马克思主义认为世界是客观存在的。不以人的意志为转移的。

对unity开发而言,unity编辑器和运行时就是世界,当然它也是客观存在的,但它的规则是它的开发者定制的,它所让我们看到的,并不一定就是真实的。

我们怎么判断一个对象是否存在?

==、!=、print、Debug.Log、Equals,一般就这些吧?可惜这些方法在C#里都是可以被重载的,可以表现得和C#定义不一致的:

unity是如何主动释放对象的?

unity是如何主动释放对象的?

unity是如何主动释放对象的?

==被重载了,上面说到的2的情况下,C#本该判断为非空的,但重载里面判断原生部分被释放后,就告诉你这个对象为null,但其实在内存中C#对象任然是存在的。。。

同样的,重载一个tostring(),你这边print、log就得不到真实情况了。至于编辑器的属性面板,那更是为所欲为,想怎么显示怎么显示了。


因此,朋友们,不要太相信这些GUI,要更相信基本的原则和内含的道理。

顺便一提,养成良好的编程习惯,善始善终,许多坑就跳过了。