0

I'm trying to access a value of a local variable inside a lambda according to this post, but I get this excepton:

MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.

Here is my code:

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
}

What am I doing wrong?

Wojtek Wencel
  • 2,257
  • 6
  • 31
  • 65
  • The method managing the `Transform` object has already destroyed it before the delegate is invoked. In other words, just because you capture a reference to `Transform` does not mean the state will be the same as when it was captured; it's just a reference and will become whatever the other reference is doing to it. – Metro Smurf Dec 01 '22 at 22:39
  • The issue is that the transform object is definitely not destroyed. The reference to it must be lost in some way, but if it actually got destroyed then I would notice the object missing in the editor. – Wojtek Wencel Dec 01 '22 at 22:40
  • 1
    `the transform object is definitely not destroyed` - sure it is; the error clearly states it has been destroyed. Track back to wherever the transform object has been created and walk the path to see where it gets destroyed after you've wired up the delegate. – Metro Smurf Dec 01 '22 at 22:43
  • It seems that it's a Unity-specific issue. The transform objects already exists as it is managed by Unity, and I can see it in the Editor, but there is no way for me to access its lifecycle. There should be no reason for it to stop existing during validation, but it seems that's what's happening. – Wojtek Wencel Dec 01 '22 at 22:48
  • You are attempting to pin `transform` via the variable `t` only to use it later during your `delayCall` callback. That's dangerous considering many types in Unity contain unmanaged resources hence the error. The effect is the same if you created a `Font` in WinForms, `Dispose`d it, then attempted to use the font. –  Dec 01 '22 at 23:01
  • Is there a better way to approach this then? – Wojtek Wencel Dec 01 '22 at 23:03
  • BTW, re the `#if UNITY_EDITOR` bit, if you want to avoid having to decorate your code with `#if`s everywhere, consider simply moving your script into a child folder called **Editor**. Unity automatically treats anything placed in such folders as _Editor-time-only_ and won't be incorporated into your final game build. Better yet, no need for the `#if` stuff. :) _[Special folder names](https://docs.unity3d.com/Manual/SpecialFolders.html)_ –  Dec 01 '22 at 23:36
  • Which line specifically is throwing the error? Where are you calling `ValidateComponent` from and what does that code look like? – Milan Egon Votrubec Dec 02 '22 at 00:02

1 Answers1

0

You are attempting to pin transform via the variable t only to use it later during your delayCall callback. That's dangerous considering many types in Unity contain unmanaged resources hence the error. The effect is the same if you created a Font in WinForms, Disposed it, then attempted to use the font.

OP:

Is there a better way to approach this then?

From what I understand, Transforms are always attached to the GameObject and it is the GameObject that is being destroyed taking the Transform along with it.

The only way I know to prevent game object destruction is viaDontDestroyOnLoad but I'm not sure if its applicable here since the latter is really designed for protection whilst transitioning to a new scene, but you never know perhaps it prevents things from being destroyed in other scenarios.

Try calling DontDestroyOnLoad at the top of ValidateComponent like so:

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

    DontDestroyOnLoad (transform.gameObject); // <---- new
    var t = transform;
.
.
.

Otherwise, there is this Unity Answer, that suggests simply disabling and re-enabling the object rather than whatever is destroying the object in the first place. Following that approach you shouldn't require any changes to your code above.

Editor-time-only scripts

BTW, regarding the #if UNITY_EDITOR bit, if you want to avoid having to decorate your code with #ifs everywhere (as an ex c/c++ programmer I certainly don't miss having to do that all the time), consider simply moving your script into a child folder called Editor. Unity automatically treats anything placed in such folders as Editor-time-only and won't be incorporated into your final game build. Better yet, no need for the #if stuff.

The best reason for it though is to prevent build errors when Unity is producing your final game due to forgetting to either use #if or Editor folders. I typically run into this one with 3rd party scripts obtained from the Unity store. Instead of having to fix the code I simply move it to a child Editor folder.

See also

  • Not sure DontDestroyOnLoad can help since it's designed to be used during runtime (it creates an additional scene) and also there is no scene reload happening during validation as far as I know. Thanks for the answer though, I'm going to accept this as I don't expect more can be said about this. – Wojtek Wencel Dec 03 '22 at 22:34
  • 1
    @WojtekWencel well I did say _”but I'm not sure if its applicable here.”_. Also, my answer also suggests simply disabling/enabling the GameObject rather than destroying it. Thanks for the accept. –  Dec 03 '22 at 23:34