Helper class used by the garbage collector to collect object references. Object的垃圾回收帮助类, 可以参考这个理解, 但没有写任何相关的
TWeakObjectPtr<>UPROPERTY()测试代码如下
// .h virtual void BeginPlay() override; UPROPERTY(BlueprintReadOnly) class AActor* Test; TWeakObjectPtr<AActor> Test2; UPROPERTY(BlueprintReadOnly) class AActor* Test3; // .cpp void AGUAO_CPlusPlusCodeGameModeBase::BeginPlay() { Super::BeginPlay(); AActor* TempActor = GetWorld()->SpawnActor<AMyActor>(); Test = TempActor; AActor* TempActor2 = GetWorld()->SpawnActor<AMyActor>(); Test2 = TempActor2; AActor* TempActor3 = GetWorld()->SpawnActor<AMyActor>(); Test3 = TempActor3; FTimerHandle TimerHandle; GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]() { Test->Destroy(); Test2->Destroy(); Test3->Destroy(); Test3 = nullptr; }, 5.f, false); FTimerHandle TimerHandle2; GetWorld()->GetTimerManager().SetTimer(TimerHandle2, [this]() { if (Test) { UE_LOG(LogTemp, Log, TEXT("Test Actor 指针存在")); if (Test->IsPendingKillPending() || Test->IsUnreachable()) { UE_LOG(LogTemp, Log, TEXT("Test Actor 指针存在但已被销毁或无法访问")); } } else { UE_LOG(LogTemp, Log, TEXT("Test Actor 指针不存在")); } if (Test2.IsValid()) { UE_LOG(LogTemp, Log, TEXT("Test2 Actor 指针存在")); if (Test2->IsPendingKillPending() || Test2->IsUnreachable()) { UE_LOG(LogTemp, Log, TEXT("Test2 Actor 指针存在但已被销毁或无法访问")); } } else { UE_LOG(LogTemp, Log, TEXT("Test2 Actor 指针不存在")); } if (Test3) { UE_LOG(LogTemp, Log, TEXT("Test3 Actor 指针存在")); if (Test3->IsPendingKillPending() || Test3->IsUnreachable()) { UE_LOG(LogTemp, Log, TEXT("Test3 Actor 指针存在但已被销毁或无法访问")); } } else { UE_LOG(LogTemp, Log, TEXT("Test3 Actor 指针不存在")); } }, 2.f, true, 10.f); }创建三个Actor, 分别用UPROPERTY()和TWeakObjectPtr<>保存, 然后销毁Actor, 之后打印Actor指针对应的信息
排除直接定义一个AActor* Test4形式, 因为会野指针崩溃.实际情况中会复杂很多, 创建销毁会在不同类里面, 甚至不同端(网络同步), 从而导致各种问题输出日志结果如下, 重点是12对比和13区别
LogTemp: Test Actor 指针存在 LogTemp: Test Actor 指针存在但已被销毁或无法访问 LogTemp: Test2 Actor 指针不存在 LogTemp: Test3 Actor 指针不存在UE4 GC会有引用记数功能, 并为了避免循环引用, 有强弱引用之分. (详情见大象无形或者网络上其他分析, 细节懒得讲也讲不清), 例如 :
A需要B, A被需要, 所以B不会被GCA只需要B, B只需要A, 但这个时候AB都不被需要, 这个时候AB应该被一起GC13对比, 3指针置空后, 相当于释放引用, 引用数归0, 正常GC, 而1此时引用数为1, 没有被GC
TWeakObjectPtr是弱引用, 不会阻止GC, 并会自动将GC后的指针至nullptr12对比, 都是直接销毁, 不做其他处理, 但2被GC并指针置空
AActor::Destory() 会销毁Actor, 但不一定会被GC1就是例子
然后这些结论会有什么用呢? 举一个可能会复杂点的例子
UPROPERTY() class AHandTool* HandTool; // 手拾取工具, 如果工具存在, 调用工具的使用方法, 如果不存在, 调用拾取方法 if (HandTool) { HandTool->Use(); } else { TryPickUpHandTool(); }一切正常, 但突然出现特殊情况, HandTool在其他地方被直接销毁了
会导致视图中工具不见了, 但这个时候指针还是存在的, 存在的, 存在的
再次执行会继续调用HandTool的Use方法, 并且不能捡起其他的HandTool
逻辑混乱了. bug了
然后查, 这里检测了他是否存在了, 看代码的时候多少会直接忽略
查不到啊? 小白无能为力了, 找老手去了
老手会试着打断点, 会发现这个时候指针还存在, 问题定位到, 解决方案呢?
一头懵, 为啥指针还存在, 不被置空呢, 谷歌各种找解决方法了
杂 : 单机的时候是很好查的, 很容易直接定位到 但如果是联网呢, 你能直接确定是指针没有被置空, 而不是没有被销毁, 不是其他逻辑出了问题, 不是网络同步的先后顺序等等 查来查去什么时候才会定位到这里这个问题吗? 联网的断点也不好打的, 打日志也不好查的. 到最后不是试着避免绕过去或者就放着不管了
别问问什么吐槽, 碰到这个问题, 挂了半年, 最后查的时候随手指针检测的时候加写了一个IsPendingKillPending()函数,引擎某些地方看到过, 然后正常了. 然后细致的想了想, 嗯, 想通了, 然而并没有什么卵用
嗯, 大概就是这样了
同时, 这个问题知道了会怎样呢, 将一些UPROPERTY()代替为TWeakObjectPtr<>吗, 很多地方不现实的 例如, 这个指针要和蓝图打交道. 例如, 这就是个蓝图变量(手动斜眼, 蓝图存在与否不知道, 但如果存在, 感觉会很糟糕, 笑)
有没有好的解决办法呢, 没有, 笑.