x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

[Closed] What's the difference between using TWeakObjectPtr or using UObject*

What's the difference between using

 TWeakObjectPtr<UObject> WeakPointer;
 WeakPointer.IsValid();

or using

 UObject *Obj;
 Obj->IsValidLowLevel();
Product Version: Not Selected
Tags:
more ▼

asked May 27 '14 at 02:35 PM in C++ Programming

avatar image

undercover
853 126 112 138

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

The question has been closed Jun 15 '14 at 03:35 PM by undercover for the following reason:

The question is answered, right answer was accepted


2 answers: sort voted first

Dear Undercover,

There's a Huge Difference, One Will Always Crash

There's a huge game-breaking difference between your two examples!


This will never crash

     TWeakObjectPtr<UObject> WeakPointer;
    if( WeakPointer.IsValid()) {}

The below code is guaranteed to crash every single time based on how you've written in it

 UObject *Obj;
 if(Obj->IsValidLowLevel()){}

reason = you are de-referencing a pointer that is pointing to nothing (dereference is the -> operator)

IsValidLowLevel()

IsValidLowLevel is not meant to be used for verifying the pointer, it is for verifying the integrity of the UObject after it is already known to exist, but might have been deleted recently. See below for how you can verify the pointer itself.

Solution

f using raw c++ pointers, you can just do this

 UObject* Obj;
 if(Obj != nullptr)
 {
   //Safe!
   Obj->GetName();
 }


The above using raw C++ pointers, and the example using .IsValid() are the two cases that are equivalent, and both are safe.

Why is this such a big deal?

Whether you understand this difference properly is what makes your game crash to desktop constantly, or run stable all the time (minus any infinite while loops hee hee) :)

Rama

more ▼

answered May 28 '14 at 04:49 AM

avatar image

Rama
10.6k 446 346 1088

avatar image undercover May 28 '14 at 03:41 PM

The question is a bit ambiguous, sorry about that. The first line's intention was just to show the type. The snippets weren't meant to be executed directly. Thanks for explaining IsValidLowLevel in more detail.

avatar image Rama May 28 '14 at 03:44 PM

Great to hear from you undercover!

:)

Rama

avatar image UnexpectedSquirt Mar 04 '17 at 09:08 PM

Sorry for the necro, but I've been using both normal object pointers (*) and TweakObjectPtrs for a while now, and I've been getting some really strange crashes when using TWeakObjectPtrs.... I always check that the pointer is valid before de-referencing it, as Rama says, but that's not enough! It still crashes with a "read access violation". When I switch to using a normal object pointer the crashes seem to go away? Has anyone else experienced this?

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

Hi undercover,

Both of the options that you mentioned perform the same essential function. However, in general TWeakObjectPtr will be the better choice if you do not need your reference to be a UPROPERTY, since it is built specifically to use with weak pointers. Just keep in mind that with a weak pointer you will never be sure if the reference is still valid, so you will always need to check it before using it.

more ▼

answered May 27 '14 at 07:15 PM

avatar image undercover May 28 '14 at 02:43 PM

Thanks a lot!

avatar image Rama May 28 '14 at 02:48 PM

Did you read my reply below? you are missing some important information

One of your examples is safe, the other can easily crash your game!

avatar image Tim C ♦♦ STAFF May 28 '14 at 02:57 PM

Rama's answer below provides a good technical explanation of the difference between these two options, and why TWeakObjectPtr is almost always going to be the better option. Regardless of which option you choose to use, always make sure to check your weak pointers before using them or you will find yourself running into crashes when testing your game.

avatar image Rama May 28 '14 at 03:01 PM

I appreciate the compliment Tim!

But there is something important that I really need to have cleared up

You wrote

"Both of the options that you mentioned perform the same essential function."

This is not true!

.IsValid() is for checking the validity of a sharedptr,

IsValidLowLevel() has the same "theme" of functionality, but is a much deeper verifying of the actual integrity of an existing UObject whose pointer is already known to be valid.

IsValidLowLevel() is so different from .IsValid(), that IsValidLowLevel() actually depends on the pointer being valid, and if it is not, IsValidLowLevel() will crash the person's game.

I just need this to be clearly understood, it is actually a very critical difference that could crash people's games to desktop if not understood properly.


An Example

If you look in Class.cpp

 // Avoid duplicate entries.
 if ( Object != NULL && !ObjectArray.Contains(Object) )
 {
   check( Object->IsValidLowLevel() );
   ObjectArray.Add( Object );
 }

The first check, checking pointer validity, is the != NULL check

The Object->IsValidLowLevel() check is a separate test that can only be safely done once the pointer is verified to be valid.

The .IsValid() check of shared ptrs is actually equivalent to the first check, the != NULL check

Even if someone does the.IsValid() check, they still need to do the next check for IsValidLowLevel() to verify internal UObject integrity.

So these are two distinct levels of verifying the data, and one depends on the other

I hope my tone is clear, I am just trying to convey an important point, and that is all :)

I have had cases in my own game where it would crash even if the .IsValid() check was performed, but not the IsValidLowLevel() check.

They are distinct and both important :)


I appreciate you Tim, and all that you do, I would not even be bothering unless this was a crash-level important matter :)

Rama

avatar image undercover May 28 '14 at 03:57 PM

I though you were safe after calling IsValid. In what cases do you need to call IsValidLowLevel, e.g. when can the UObject become invalid. Thanks a lot for explaining this!

avatar image Rama May 28 '14 at 04:03 PM

In my in-game level editor, I have had cases where .IsValid() passed, but if I tried to use the UObject the game would crash, because the UObject was in the process of being deleted.

If the destroying of an object has already begun, but not finished, its pointer can still be valid, but the integrity of the UObject data has been compromised.

So in my case it involved UObjects that had been recently deleted, and I was still trying to use them every tick, or access their data them in some way.

It was only when I added the IsValidLowLevel() check, in addition to verifying the pointer, that my game stopped crashing.

My game would not be functional right now if I did not find out about IsValidLowLevel(), as distinct from simply verifying the pointer

Example:

 if(SomeObject.IsValid())
 {
   if(SomeObject->IsValidLowLevel()) //if not included, game sometimes crashed
  {
     //Actually do stuff with SomeObject
  }
 }

My specif case was UMaterialInstanceDynamic's that I was periodically deleting, but my HUD class was checking them every tick using only the simple pointer validity test.

Adding the additional check of IsValidLowLevel() completely removed those periodic crashes and was a make-or-break moment for my project :)

I had crash call stacks showing me down to the line where it was happening, AFTER the IsValid() check had passed successfully!

avatar image undercover May 28 '14 at 04:15 PM

Okay, I understand what you mean now =) Thanks a lot for your effort explaining this!

avatar image ColdSteel48 Nov 06 '18 at 03:44 AM

@Rama

I suppose it is different now ?

 /**  
  * Test if this points to a live UObject
  * @param bEvenIfPendingKill, if this is true, pendingkill objects are considered valid
  * @param bThreadsafeTest, if true then function will just give you information whether referenced 
  *                            UObject is gone forever (@return false) or if it is still there (@return true, no object flags checked).
  * @return true if Get() would return a valid non-null pointer
 **/
 FORCEINLINE bool IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest = false) const
 {
     return TWeakObjectPtrBase::IsValid(bEvenIfPendingKill, bThreadsafeTest);
 }


 /**
  * Test if this points to a live UObject. This is an optimized version implying bEvenIfPendingKill=false, bThreadsafeTest=false.
  * @return true if Get() would return a valid non-null pointer
  */
 FORCEINLINE bool IsValid(/*bool bEvenIfPendingKill = false, bool bThreadsafeTest = false*/) const
 {
     return TWeakObjectPtrBase::IsValid();
 }

From the comments above of the engine code it also checks if PendingKill

Which leads to this Function:

 /** Private (inlined) version for internal use only. */
 FORCEINLINE_DEBUGGABLE bool Internal_IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest) const
 {
     if (ObjectSerialNumber == 0)
     {
         checkSlow(ObjectIndex == 0 || ObjectIndex == -1); // otherwise this is a corrupted weak pointer
         return false;
     }
     if (ObjectIndex < 0)
     {
         return false;
     }
     FUObjectItem* ObjectItem = GUObjectArray.IndexToObject(ObjectIndex);
     if (!ObjectItem)
     {
         return false;
     }
     if (!SerialNumbersMatch(ObjectItem))
     {
         return false;
     }
     if (bThreadsafeTest)
     {
         return true;
     }
     return GUObjectArray.IsValid(ObjectItem, bEvenIfPendingKill);
 }
(comments are locked)
10|2000 characters needed characters left
Viewable by all users

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question