捕获Lambda中变量时触发MissingReferenceException问题排查
问题(翻译后)
我参考了一篇Stack Overflow帖子尝试在Lambda中访问局部变量的值,却触发了如下异常:
MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.
我的代码如下:
private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component { #if UNITY_EDITOR // EditorApplication is only available when running in the editor if (EditorApplication.isPlaying) return; if (transform == null) return; // in case inspector field is not assigned var t = transform; EditorApplication.delayCall += () => { var component = t.GetComponent<T>(); if (component == null) { LogAddingComponent<T>(t); var addedComponent = t.gameObject.AddComponent<T>(); validationMethod.DynamicInvoke(args.Prepend(addedComponent)); } else { validationMethod.DynamicInvoke(args.Prepend(component)); } }; #endif }
请问我哪里出错了?
问题原因
你通过var t = transform;复制的只是引用地址,而非Transform对象本身——因为Transform是Unity的引用类型。当EditorApplication.delayCall执行时(下一次编辑器刷新时),原Transform对应的GameObject可能已经被用户删除或销毁,此时t的引用指向的是一个已标记为销毁的对象,就会触发MissingReferenceException。
另外,Unity编辑器中对象销毁后,引用不会立刻变为null(这是编辑器的“虚引用”机制,用于提示对象已销毁),所以你注册delayCall前的if (transform == null)判断,无法覆盖“注册后对象才被销毁”的场景。
解决方法
方案1:通过实例ID验证对象存活
注册delayCall前记录对象的实例ID,执行时通过ID查找对象,确认是否还存在:
private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component { #if UNITY_EDITOR if (EditorApplication.isPlaying) return; if (transform == null) return; var instanceId = transform.GetInstanceID(); var originalArgs = args; var targetMethod = validationMethod; EditorApplication.delayCall += () => { // 通过实例ID查找对象,判断是否存活 var t = EditorUtility.InstanceIDToObject(instanceId) as Transform; if (t == null) return; var component = t.GetComponent<T>(); if (component == null) { LogAddingComponent<T>(t); var addedComponent = t.gameObject.AddComponent<T>(); targetMethod.DynamicInvoke(originalArgs.Prepend(addedComponent)); } else { targetMethod.DynamicInvoke(originalArgs.Prepend(component)); } }; #endif }
方案2:使用弱引用持有对象
利用WeakReference持有对象引用,既不会阻止对象被销毁,又能在执行时检查对象状态:
private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component { #if UNITY_EDITOR if (EditorApplication.isPlaying) return; if (transform == null) return; var weakTransform = new WeakReference(transform); var originalArgs = args; var targetMethod = validationMethod; EditorApplication.delayCall += () => { if (!weakTransform.IsAlive) return; var t = weakTransform.Target as Transform; if (t == null) return; var component = t.GetComponent<T>(); if (component == null) { LogAddingComponent<T>(t); var addedComponent = t.gameObject.AddComponent<T>(); targetMethod.DynamicInvoke(originalArgs.Prepend(addedComponent)); } else { targetMethod.DynamicInvoke(originalArgs.Prepend(component)); } }; #endif }
额外注意点
- 要把
validationMethod和args也复制到局部变量(如示例中的originalArgs、targetMethod),避免外部上下文变化导致的意外问题。 - 编辑器环境下
delayCall的执行时机存在时间差,这段时间内用户完全可能修改或销毁对象,所以必须做有效性校验。
内容的提问来源于stack exchange,提问作者Wojtek Wencel
相关产品推荐
相关产品推荐

