Announcement

Collapse
No announcement yet.

UPROPERTY member vars reset to NULL by ObjectInitializer

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    UPROPERTY member vars reset to NULL by ObjectInitializer

    Hello devs,

    does anybody have the same problem, UPROPERTY vars of an actor being reset to NULL after the constructor finishes?

    Check out more details here:

    https://answers.unrealengine.com/que...ible-in-c.html

    I need the UPROPERTY because I want the components to be editable in the editor.. is there a switch in the UPROPERTY macro that prevents the var from being reset? Or is this a bug?

    #2
    I take it from the context of that AnswerHub question that this object of yours is blueprinted? It seems as though the CDO for that blueprint was saved with a null component. Thus, after running the constructor, the defaults are copied over to your object and the component is nullified. You should be able to tell if this is the case by getting the object instance somewhere in your code and looking at it with the debugger.

    Components should be VisibleAnywhere, VisibleInstanceOnly or VisibleDefaultsOnly, never editable. Components being pointers, an editable component means you can edit the pointer to the component itself rather than the contents of the component, for instance allowing the component to be nullified. Which might be what somehow happened here along the way.

    If this theory is correct, in order to get rid of the nullified component, you'll have to get rid of the current blueprint CDO. Unfortunately where this CDO exists is something I'm not clear on. I think part of it is in the blueprint asset itself, and some derived data is also processed in the Derived Data Cache. Last time I've had issues with undesirable defaults saved to a blueprint CDO, I managed to clear it up by deleting the blueprint, deleting the DDC folders (Engine/DerivedDataCache + MyGame/DerivedDataCache) and then recreating.

    -Camille

    Comment


      #3
      You actually have to attach it to the actor, otherwise it will just be garbage collected.

      Either set it as root

      Code:
      RootComponent = PlatformComponent;
      or attach it to the existing root (or any other component)

      Code:
      PlatformComponent->AttachParent = RootComponent;

      *edit: also posted on answerhub
      Last edited by DennyR; 01-30-2015, 12:22 PM.

      Comment


        #4
        Originally posted by DennyR View Post
        You actually have to attach it to the actor, otherwise it will just be garbage collected.
        No.

        Garbage collection does not occur that early, for starters.

        If all components needed to be attached in order to not be garbage collected, then non-scene components would all get garbage collected, since they cannot be attached.

        There already is a managed reference to the component which should prevent it from being collected:

        Code:
        UPROPERTY()
        USceneComponent* PlatformComponent;
        Lastly, if it were a case of garbage collection somehow destroying the component, then it wouldn't get nullified. The pointer would keep its value but point to a now dead object, with likely disastrous results.

        You're right in that the scene component needs to be attached somewhere in the scene in order to be displayed properly, but this is not what's causing this issue.

        Comment


          #5
          Originally posted by cmartel View Post
          I take it from the context of that AnswerHub question that this object of yours is blueprinted? It seems as though the CDO for that blueprint was saved with a null component. Thus, after running the constructor, the defaults are copied over to your object and the component is nullified. You should be able to tell if this is the case by getting the object instance somewhere in your code and looking at it with the debugger.

          Components should be VisibleAnywhere, VisibleInstanceOnly or VisibleDefaultsOnly, never editable. Components being pointers, an editable component means you can edit the pointer to the component itself rather than the contents of the component, for instance allowing the component to be nullified. Which might be what somehow happened here along the way.

          If this theory is correct, in order to get rid of the nullified component, you'll have to get rid of the current blueprint CDO. Unfortunately where this CDO exists is something I'm not clear on. I think part of it is in the blueprint asset itself, and some derived data is also processed in the Derived Data Cache. Last time I've had issues with undesirable defaults saved to a blueprint CDO, I managed to clear it up by deleting the blueprint, deleting the DDC folders (Engine/DerivedDataCache + MyGame/DerivedDataCache) and then recreating.

          -Camille
          Thanks, this seems to be what is happening! I'll try your suggestions with re-creating the blueprint.


          But then I have a different question: How do I make the contents of the component editable? Or is this achieved by simply having the component as a public property?

          Comment


            #6
            Originally posted by chaosgrid View Post
            But then I have a different question: How do I make the contents of the component editable? Or is this achieved by simply having the component as a public property?
            With regards to public vs. private properties, I'm not 100% positive on this as the behaviour was changed in UE4.6 and we're still on 4.5. 4.5 didn't care about member visibility, but 4.6 seems to enforce it to an extent. From what I've seen discussed on the forums, I think it works like this:

            Recommended practice is now to make components private and provide a getter function should they be accessed from outside the class. Like making component properties non-editable, this is to prevent components from being overwritten by "outside code". This used to be protected through the TSubobjectPtr template, but this has been deprecated.

            As mentioned earlier, making the component Visible seems to be all it takes for the component's properties to be editable. I think this needs to specifically be VisibleAnywhere for the editor to handle the inline component property display.

            All of this seems corroborated by the code in 4.6. Take a look at AStaticMeshActor for instance:
            Code:
            private_subobject:
            	DEPRECATED_FORGAME(4.6, "StaticMeshComponent should not be accessed directly, please use GetStaticMeshComponent() function instead. StaticMeshComponent will soon be private and your code will not compile.")
            	UPROPERTY(Category = StaticMeshActor, VisibleAnywhere, BlueprintReadOnly, meta = (ExposeFunctionCategories = "Mesh,Rendering,Physics,Components|StaticMesh", AllowPrivateAccess = "true"))
            	class UStaticMeshComponent* StaticMeshComponent;
            
            (...)
            
            public:
            	/** Returns StaticMeshComponent subobject **/
            	class UStaticMeshComponent* GetStaticMeshComponent() const;
            I'm guessing private_subobject is a macro that currently is public but will change to private once this deprecation is applied.

            So, short version, try private and VisibleAnywhere.

            Comment


              #7
              Originally posted by chaosgrid View Post
              How do I make the contents of the component editable? Or is this achieved by simply having the component as a public property?
              I haven't tested, but my suspicion is that it's not related to the public/private declaration. So long as the component is visible, then the ability to edit its contents (ie. its own properties) is determined by the specifiers that were set on the properties within the component class itself.

              Originally posted by cmartel View Post
              It seems as though the CDO for that blueprint was saved with a null component. Thus, after running the constructor, the defaults are copied over to your object and the component is nullified... If this theory is correct, in order to get rid of the nullified component, you'll have to get rid of the current blueprint CDO. Unfortunately where this CDO exists is something I'm not clear on.
              Maybe it's more complicated than I imagine, I am very new to UE4. From the way I understood things though, the CDO of a blueprint class is simply defined by the values set in the defaults section of that blueprint (and up the chain of its parent classes). A CDO is just an object with property values unchanged from the defaults defined by its class, right? In which case, if you're right in your suspicion of the issue, then the following should work:
              1. Temporarily make the component property EditAnywhere.
              2. Reload the editor and open the blueprint in question.
              3. In its defaults, find the component. It would be set to None.
              4. Click the yellow arrow to reset it to the default from its parent class CDO, which should be the value set in the C++ constructor.
              5. Save and exit. Change EditAnywhere back to VisibleAnywhere.

              Comment


                #8
                Thanks guys, you were correct. I simply had to define it as VisibleAnywhere and prior to that reset it to the default (it was None!) in the defaults tab of the Blueprint! Also, having the components as private vars still shows the contents in the editor just fine.

                Comment


                  #9
                  4.7.3 - still there

                  We are also experiencing this on 4.7.3.
                  What our arrangement boils down to is really simple (pseudocode-ish for brevity).
                  Click image for larger version

Name:	codesample.PNG
Views:	1
Size:	42.1 KB
ID:	1071587

                  I asked cmartel for help in PM and later decided to move the conversation here.
                  Originally posted by cmartel
                  How are you overriding the parent class's component? It's hard to tell without a code example, but it sounds like you're overriding it in the derived constructor, which won't work.
                  Thank you for pointing that out, I didn't know that. But we are not overriding the component.
                  Our issue is exactly like described here or here. We haven't had our project upgraded from 4.3.1 in a loong time, and this issue started appearing only when we introduced StatComponent (on the same engine version).
                  Our Character-derived assets seem to have been corrupted with null references and most of them (or all) had an empty StatsComponent tab in the editor. Any code access to StatsComponent outside of constructor code resulted in an obvious crash. I tried to fix this by doing what chaosgrind describes (setting Component uproperty to EditAnywhere, BlueprintReadWrite, then resetting assets to their default, then modifying back to VisibleAnywhere, BlueprintReadOnly) to find out that I couldn't reset the component variable from None - there were no alternative choices. Removing StatsComponent from the game entirely was of little help - Stats Component variable on instances was greyed out and set to 'None'.
                  Eventually, after some heavy tinkering, updating the engine, tinkering and saving a bit more and finally giving up and re-creating assets, StatsComponent started to spawn correctly for a few launches. Then it broke again.
                  What I (and many others) found out was that the value for the new component pointer has been overwritten by the Blueprint class CDO. (testTriangleWander is a Bot blueprint)
                  Click image for larger version

Name:	cdo_null.png
Views:	1
Size:	365.0 KB
ID:	1071585

                  I couldn't reproduce the issue on a clean game with this class inheritance arrangement. My next guess is cyclic dependencies in blueprints, but I haven't yet found any. Is casting to Bot inside Character blueprint considered a cyclic dependency?
                  Also, I should mention that I have occasional failures on saving a map (externally referenced error) or a blueprint (same here, externally referenced material instances mostly). But these are easy as pie to fix. Sometimes I have "LogUObjectGlobals:Warning: Failed to find object 'Class None.'" logged to me once but I haven't tracked it down yet.
                  I'm workin on this for about 3 days straight and the deeper I go, the more cryptic it seems to me. I have decided to take a break from this issue for a day before trying harder I am now compiling the bleeding edge engine for rurther testing.

                  Text version of pseudocode for convenience:
                  Code:
                  //StatsComponent.h
                  class UStatsComponent : public UActorComponent
                  {
                  	GENERATED_BODY()
                  public:
                  	UStatsComponent(const class FObjectInitializer& ObjectInitializer);
                  	/** character stats, with items and power-ups applied. */
                  	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Stats)
                  	class URPGStats* Stats; //bare UObject child
                  }
                  
                  //StatsComponent.cpp
                  UStatsComponent::UStatsComponent(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
                  {
                  	Stats = NewObject<URPGStats>(this);
                  	check(Stats); //always valid
                  }
                  
                  //MyCharacter.h
                  class AMyCharacter : public ACharacter
                  {
                  	GENERATED_BODY()
                  public:
                  	/** Character stats managing component */
                  	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Stats)
                  	class UStatsComponent* StatsComponent;
                  }
                  
                  //MyCharacter.cpp
                  AMyCharacter::AMyCharacter(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
                  {
                  	StatsComponent = ObjectInitializer.CreateDefaultSubobject<UStatsComponent>(this, TEXT("CharacterStats"));
                  	check(StatsComponent); //always valid here
                  }
                  
                  //MyBot.h
                  class AMyBot : public AMyCharacter
                  {
                  	GENERATED_BODY()
                  public:
                  	AMyBot(const class FObjectInitializer& ObjectInitializer);
                  protected:
                  	void BeginPlay() override;
                  }
                  
                  //MyBot.cpp
                  AMyBot::AMyBot(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
                  {
                  	check(StatsComponent); //always valid
                  }
                  
                  void AMyBot::BeginPlay()
                  {
                  	check(StatsComponent); //!CRASH
                  }

                  Comment


                    #10
                    Originally posted by cmartel View Post
                    With regards to public vs. private properties, I'm not 100% positive on this as the behaviour was changed in UE4.6 and we're still on 4.5. 4.5 didn't care about member visibility, but 4.6 seems to enforce it to an extent. From what I've seen discussed on the forums, I think it works like this:

                    Recommended practice is now to make components private and provide a getter function should they be accessed from outside the class. Like making component properties non-editable, this is to prevent components from being overwritten by "outside code". This used to be protected through the TSubobjectPtr template, but this has been deprecated.

                    As mentioned earlier, making the component Visible seems to be all it takes for the component's properties to be editable. I think this needs to specifically be VisibleAnywhere for the editor to handle the inline component property display.

                    All of this seems corroborated by the code in 4.6. Take a look at AStaticMeshActor for instance:
                    Code:
                    private_subobject:
                    	DEPRECATED_FORGAME(4.6, "StaticMeshComponent should not be accessed directly, please use GetStaticMeshComponent() function instead. StaticMeshComponent will soon be private and your code will not compile.")
                    	UPROPERTY(Category = StaticMeshActor, VisibleAnywhere, BlueprintReadOnly, meta = (ExposeFunctionCategories = "Mesh,Rendering,Physics,Components|StaticMesh", AllowPrivateAccess = "true"))
                    	class UStaticMeshComponent* StaticMeshComponent;
                    
                    (...)
                    
                    public:
                    	/** Returns StaticMeshComponent subobject **/
                    	class UStaticMeshComponent* GetStaticMeshComponent() const;
                    I'm guessing private_subobject is a macro that currently is public but will change to private once this deprecation is applied.

                    So, short version, try private and VisibleAnywhere.
                    Thanks for the info. To supplement your advice, in order for the components to be private members marked as UPROPERTYs, the parameter meta = (AllowPrivateAccess = "true") is required. Otherwise, a compiler error will be produced:

                    BlueprintReadOnly should not be used on private members
                    Is there any documentation on the meta specifiers for UPROPERTY? I searched several times for this, but couldn't find anything.

                    Comment


                      #11
                      The biggest thing that stands out is this:

                      Code:
                      //StatsComponent.cpp
                      UStatsComponent::UStatsComponent(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
                      {
                      	Stats = NewObject<URPGStats>(this);
                      	check(Stats); //always valid
                      }
                      Objects constructed in this fashion will not have the proper flags set that mark it as a subobject for serialization. I suspect what is happening here is that the constructor is invoked, then in ~FObjectInitializer, property values are serialized in. Since Stats is not properly marked as a subobject, it has no serialized value and ends up being zeroed out. See if you absolutely need a subobject, something like "stats" is usually best represented as a struct. Especially in this case where StatsComponent seems to be just a container for the stats data. If you do want an object, then make use of that object initializer and use CreateDefaultSubobject. You will also need to mark the Stats property as Instanced, otherwise all your stats components will share the same Stats subobject.

                      Now, that probably doesn't explain why the StatsComponent itself is getting reset to null. Most likely Blueprint serialization is involved. Have you tried recreating a different blueprint from scratch to see if it also happens? If it persists, use a data breakpoint to see when it's being overwritten. (In Visual Studio, use the debugger to break in UStatsComponent::UStatsComponent, right after Stats is assigned. Then in the breakpoints window, use New Data Breakpoint and provide &Stats.) The debugger should break when the value gets overwritten, which may or may not help clarifying. My educated guess is that it will break inside FObjectInitializer::~FObjectInitializer, in InitProperties or possibly InstanceSubobjects.

                      to find out that I couldn't reset the component variable from None - there were no alternative choices
                      Creating subobjects inline is finnicky and certainly doesn't work for components. You shouldn't be trying to use the drop down menu, but should rather be using the yellow arrow to revert to defaults. But if the Blueprint's default object is serialized with a null component, as seems to be the case, then the default value for your blueprint is in fact null and using the yellow arrow (if it even shows up) will just revert to None. You would have to fix this by reverting the blueprint defaults to that of the native base class.

                      Unfortunately, this is where my experience breaks down -- I've never confirmed where the serialized data for blueprint CDOs lies. Since they are an asset type, my theory is that they go through the DDC (derived data cache) like all other assets. When I last had issues serializing blueprint defaults, I fiddled with lots of different things like recompiling, resaving, recreating, as well as deleting the contents of the DDC. I'm not sure which part worked but the DDC might be worth a try. Delete or rename the DerivedDataCache folder for your game project, you might also have to do it for the Engine DerivedDataCache folder.

                      Edit: Well ****, I just realized I already said most of this about the DDC earlier in this thread.

                      Originally posted by zaha View Post
                      Is there any documentation on the meta specifiers for UPROPERTY? I searched several times for this, but couldn't find anything.
                      You'll find most of them in ObjectBase.h. But unlike the class/property/function keywords, metadata keywords aren't lexically parsed by the header tool, so they don't need a definition in ObjectBase and therefore get forgotten from time to time... As AllowPrivateAccess was. For the most part, they can be figured out by searching the codebase to see how they are used, though.

                      -Camille
                      Last edited by cmartel; 03-31-2015, 11:31 AM.

                      Comment

                      Working...
                      X