Search Unity

Check if an object is marked for destruction

Discussion in 'Scripting' started by Wahooney, Nov 2, 2021.

  1. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    Why is it that in Unity 2020.3.20f it is still not possible to check that an object is marked for destruction?

    I've tried EVERYTHING, and none of it works flawlessly.

    I have a ScriptableObject, with HideFlags.HideInHierarchy | HideFlags.DontSave (I've tried without these flags and it's still a problem) and after hitting play and stopping, I get the following in editor:
    • Any time I try to access it through a variable the console reports that it's been destroyed
    • When I do a Debug.Log it says it's null
    • When I do a (myObject == null), it's always false
    • The advice in this post always returns false (and seems to be another implementation of Object.Equals() checks).
    So the object is obviously marked for destruction, but isn't actually destroyed/released from memory. AFAIK the flags I'm using shouldn't be interfering here. I've also had this problem with a reference to a custom Editor (which doesn't have these flags), but I was able to work around that issue with a stupid hack that won't work with my ScriptableObject.

    Why does myObject == whatever and myObject.DoThing() have different results when checking null/destroyed status?

    Please note that I'm not destroying this object manually. So the make-your-own-destroyer solution won't work either.

    This should be a trivial problem to solve on Unity's end (give us something like IsValid(object) in Unreal Engine which checks for null status and marked for destruction status), but every time the issue comes up the official response we get is: That's not the Unity Way™!

    Apparently the Unity Way™! is to deny developers the most basic tools they need to do their work.

    My only work around at this point is rebuilding my source, and that's no solution at all.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    What type is myObject declared as (the variable, not the object the variable references)?

    Which advice, there is a lot of different things said in that thread.

    ...

    Can you share whatever code you've written that is resulting in what you're seeing?

    Because the '== null' should return true if the variable is typed as a UnityEngine.Object (or child type like Component or MonoBehaviour), and the object is being destroyed (where cases 1 and 2 in your list occur).

    ...

    Lastly... technically there is a method like what you ask. It's just marked private:
    https://github.com/Unity-Technologi.../Scripting/UnityEngineObject.bindings.cs#L122

    As you can see it is indirectly called via either the ==, !=, or bool cast operators of UnityEngine.Object.

    Personally I reflect out this method and create a delegate from it to call it. This technically works, but could potentially break in newer versions of Unity if they change the name of the method ever. Hence why my logic creates a backup using the != operator in case it fails to find it:
    https://github.com/lordofduct/space...SpacepuppyUnityFramework/Utils/ObjUtil.cs#L43

    Code (csharp):
    1.             try
    2.             {
    3.                 var tp = typeof(UnityEngine.Object);
    4.                 var meth = tp.GetMethod("IsNativeObjectAlive", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
    5.                 if (meth != null)
    6.                 {
    7.                     var d = System.Delegate.CreateDelegate(typeof(System.Func<UnityEngine.Object, bool>), meth) as System.Func<UnityEngine.Object, bool>;
    8.                     _isObjectAlive = (a) => !object.ReferenceEquals(a, null) && d(a);
    9.                 }
    10.                 else
    11.                     _isObjectAlive = (a) => a != null;
    12.             }
    13.             catch
    14.             {
    15.                 //incase there was a change to the UnityEngine.dll
    16.                 _isObjectAlive = (a) => a != null;
    17.                 UnityEngine.Debug.LogWarning("This version of Spacepuppy Framework does not support the version of Unity it's being used with. (ObjUtil)");
    18.                 //throw new System.InvalidOperationException("This version of Spacepuppy Framework does not support the version of Unity it's being used with.");
    19.             }
     
    Last edited: Nov 2, 2021
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    If you are destroying something, this means you have a reference to it.

    When you destroy it, set that reference to null.

    I'm not sure when that wouldn't work flawlessly.

    I imagine it might not work if you have lots of different places all referring to the same object, but this seems like a bad idea in general, as you are repeating yourself (DRY).
     
  4. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    Thanks for replying.

    If I'm understanding you correctly: the object and the variable reference are the same class.

    Every solution that actually checks the null / destroyed status (I tried solutions from other posts, but they are all basically the same with minor differences, that was just the most recent post I tried). I started the custom destroy method but then I realised I'm not the one actually killing the object.

    Unfortunately not, the code is proprietory and not my property to share, even in part.

    There is no casting or conversion in my issue, the class my object is created from is only inherited from ScriptableObject and nothing is inherited from it.
     
  5. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    I'm not the one destroying it, it's somewhere inside Unity when I stop playing.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    Can you recreate the problem in "like" code. Basically write a simple example project that demonstrates the problem you're having.

    When I'm having problems where the code is not behaving the way I think it aught to. I try to recreate it in a clean room like setting (outside of my main project where the chaos of everything happening may cloud what is actually going on).

    In doing so either 1) I can demonstrate a bug with the system and now have an example project to submit to Unity as a bug report. Or 2) it works and I now have a working example to contrast against my in game broken code and try to find what I'm missing based on that.
     
    Kurt-Dekker likes this.
  7. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    I'm busy with that at the moment, but it's a pretty tricky situation to repro cleanly but I think I'm getting there.

    The fact remains that regardless of how I got to this bug: a proper, consistent solution to test an object's validity is essential (since referencing and null checking can yield completely contradictory results), because I've encountered this problem at least 20 times in different projects and sometimes there are unique solutions required (like this one) that could easily be solved with a IsValid(myObject) function that tests null state and marked for deletion state, which should be trivial for Unity to add, and has been discussed/requested for years.
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    As stated and linked... there are ways:

    UnityEngine.Object == operator
    UnityEngine.Object != operator
    UnityEngine.Object bool cast operator

    If they are not behaving consistently for you, then we need to determine why. This is why I asked for code, or reproductive code.
     
    AndrewSkow likes this.
  9. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    The equality operations all tell me the object is not null, casting the object to Unity.Object or System.Object and doing null checks on the cast objects also tells me they aren't null.

    The inconsistency comes in between the null checks and actually dotting onto the object, the former tells me the object exists in a valid state, the latter tells me the object is in an invalid state. I even tried casting to a System.String to see if it casts to "null" and comparing to that, but that told me the object was fine. That said:

    Code (CSharp):
    1. object.ToString()
    tells me it's destroyed and can't be accessed. Where:

    Code (CSharp):
    1. object as string
    casts to what you'd expect from a valid object.

    It's all very bizarre. (still working on that clean example)
     
  10. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    I don't understand what you're saying cause...

    If 'object' is a unity object (the only kinds of objects that can be destroyed).

    Then the first:
    Code (csharp):
    1. object.ToString()
    This would either create a string, or it would fail if object is null, or object is destroyed.

    Code (csharp):
    1. object as string
    Would result in null since 'object' is a unity object and string is NOT a unity object. Any time you cast using the 'as' operator to a type that it can't be cast to, null is the result.

    ...

    Rereading this a few times. I think you're saying you do a null check, and if it's not null, and if it's not you access it, but then upon accessing it, it reports its destroyed:
    Code (csharp):
    1. if(obj != null)
    2. {
    3.     obj.transform.position = Vector3.zero; //just an example
    4. }
    Is this so?

    If so... that means the null check isn't working.

    That's weird... because that means ==/!= is not behaving the way its supposed to.

    Which is why I ask for code exemplifying it.

    Because as is...

    there's 2 options

    1) There's a really glaring bug on Unity's side in a often used operator that EVERYONE uses... which is weird (bugs in things that are used by everyone usually would get lots of posts about it).

    2) There is a user error on your side. Such as if 'obj' is typed as say 'System.Object'... which the == overload won't work on because the compiler doesn't know it's a UnityEngine.Object.

    ...

    But I can't say what it is until you show me code that exemplifies the problem you're having.

    Heck, you haven't even showed us the exception/error you receive.
     
    Last edited: Nov 3, 2021
    Bunny83 and MelvMay like this.