Unity对象生命周期
全库目录221
本文目录8
Unity对象比较为null时,C#包装引用仍可能存在,因为原生对象已经销毁。创建与Destroy涉及引擎注册、回调和资源引用,不等于new或托管GC。理解两层身份才能正确判断释放与复用。
版本与对象身份
本文按Unity 6000.0的Object.operator ==和Object.Destroy文档解释生命周期,2026-10-22核读。C#示例为机制片段,完整Unity工程编译与运行尚未执行。
Unity Object的两层身份
GameObject、Component、Texture、Mesh 等 UnityEngine.Object 通常有托管包装对象和引擎原生对象。C# 引用释放只影响托管对象可达性,原生资源的释放还由引擎对象与资源所有权决定。
Unity 特殊的 Null 比较会检查原生对象是否已被销毁。对象看似 == null 时托管包装对象仍可能存在,普通 C# ReferenceEquals 语义不同。
Instantiate 的成本
设一个Prefab包含根节点、两个子节点、Renderer与用户脚本。实例化时,先要复制这组对象及序列化字段,让副本的层级和内部引用指向副本;随后对象进入相应子系统,启用回调又可能执行用户逻辑。因此一次创建可以沿下面几类工作定位:
- 托管包装对象与脚本对象分配;
- 原生GameObject、Transform、Component 分配;
- Prefab与序列化字段复制与引用解析;
- 变换层级注册;
- Renderer、Physics、Animation、Audio 等子系统注册;
Awake、OnEnable等生命周期回调;- 脚本在回调中继续分配、查找和加载;
- 后续渲染/物理结构更新。
成本不是“new 一个 C# 对象”。复杂 Prefab 的 Component、子层级和回调往往比单次托管分配成本更高。
GPU资源是否在此刻创建或上传取决于资产与引擎状态,不能笼统说每次 Instantiate 都上传全部 Mesh/Texture。
Destroy 的成本
Unity Destroy安排对象销毁。Unity 6000.0文档规定,实际销毁在当前Update循环结束以后、渲染以前发生;带延时参数时还要等待指定时间。销毁可能触发:
OnDisable/OnDestroy;- 层级关系和子系统注销;
- 原生资源引用变化;
- 托管包装对象随后由GC回收;
- 批量销毁造成帧末峰值。
DestroyImmediate 主要用于受控编辑器场景,运行时或遍历集合期间调用可能破坏状态。
原生资产是否卸载还取决于其他引用、AssetBundle/Addressables Handle 和资源管理策略。
销毁后的有效性检查
保存一个UnityEngine.Object引用,销毁它,再跨过销毁处理时机,能够观察两种身份的区别。以下C#协程片段放在一个持续存活的MonoBehaviour中;它只销毁自己创建的测试对象,不销毁协程宿主。
System.Collections.IEnumerator ProbeLifetime()
{
var target = new UnityEngine.GameObject("LifetimeProbe");
UnityEngine.Object wrapper = target;
UnityEngine.Object.Destroy(target);
yield return null;
UnityEngine.Debug.Log(wrapper == null); // 预期true
UnityEngine.Debug.Log(object.ReferenceEquals(wrapper, null)); // 预期false
}协程保留包装引用,所以CLR仍能观察到它;Unity重载的比较还检查底层原生对象,因而判为无效。?.和??采用C#引用空值语义,不能替代Unity对象有效性检查。访问引擎属性以前显式判断Unity对象,可避免拿着尚存的包装引用调用已销毁对象。
对象停用后,包装与原生对象仍然存在;禁用不是销毁。池中对象因此仍占有组件和资源,复用时要重置事件订阅、动画和物理状态,并按具体API管理协程和异步请求的结束。
假设池中敌人E在第7次借出期间请求头像,请求尚未完成就归还;第8次借出又把同一对象分配给另一个敌人。旧请求完成时,E仍有效,Unity空值检查当然通过,却会把旧头像覆盖到新敌人。让借出代数每次递增,请求捕获7,完成时要求target仍有效 && currentGeneration==7。当前已为8就丢弃交付,并释放该请求自己的资源句柄。取消用于减少无用工作,代数检查用于拒绝迟到结果,二者承担不同职责。
验证方法
分别记录Instantiate、Activate、Destroy与原生资源释放时间。销毁后比较ReferenceEquals与引擎null检查,测试场景卸载和Asset句柄未释放。实际Unity运行尚未验证。
相关主题
参考资料
- Unity 6000.0 Object.operator ==,Description中的托管与原生对象检查;2026-10-22核读。
- Unity 6000.0 Object.Destroy,Description中的销毁时机、组件与子对象行为;2026-10-22核读。