강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

가루님의 프로필 이미지
가루

작성한 질문수

이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해

8강 언리얼 C++ 설계 II - 컴포지션

TObjectPtr 과 일반 포인터에 대한 용도 질문

해결된 질문

작성

·

46

·

수정됨

0

안녕하세요 좋은강의 만들어주셔서 감사합니다.

8강 16분 40초 부터 있는 TObjectPtr에 대한 질문입니다.

TObjectPtr을 쓰는 이유는 해당 생성한 객체를 언리얼에서 더 안전하게 관리하고 GC추적에 용이하기 때문에 쓰는 것으로 이해 했습니다. 그리고 구현부 cpp파일에서는 임시로 사용하고 바로 해제하기 때문에 일반 포인터를 사용하는 것으로도 이해 했습니다.

 

그래서 궁금한 점 질문드립니다.

클래스 내부에서 NewObject 직접 생성한 클래스가 아닌 외부에서 참조한 즉 캐싱용도인 멤버변수로 저장하는 경우에도 TObjectPtr을 쓰는게 맞을까요?

예를들어 GameInstance는 싱글톤 형식이라

GetWorld()->GetGameInstance()이런식으로 접근하던데 그렇다는 것은 언리얼 내부 어딘가에서 GameInstance를 생성하고 이를 TObjetPtr 스마트 포인터 형식으로 관리를 하고 있을것이다 생각했습니다.

그랬을떄 만약 어떤 클래스에서 매 프레임GetWorld()->GetGameInstance() 호출하는 대신 효율을 위해 한 번만 가져와 멤버 변수에 캐싱 하는 경우를 가정하면

이때는 해당 클래스가 생명주기를 직접 책임지는 것도 아니고 GC가 따로 추적할 이유도 없어보이고 단순 캐싱용도로만 저장했기때문에 이런 목적에는 TObjectPtr 대신 일반 포인터를 써도 되지 않을까 생각이 들었습니다.

아무래도 해당 클래스에서 생성한 객체가 아니고 단순히 빠르게 접근하기위한 캐싱용도로만 저장했기 때문에 TObjectPtr로 굳이 넣을 필요가 있을까? 의문이 들었습니다.

 

어떤 방법이 맞을까요?

답변 2

0

이득우님의 프로필 이미지
이득우
지식공유자

안녕하세요.
TObjectPtr은 언리얼 에디터에 필요한 기능 ( 레퍼런스 조사, 애셋 로딩이 완료되었는지 파악 등등.. ) 을 위해 5버전부터 도입된 구조체인데, 가비지 컬렉션 관련해서는 일반 포인터와 동일하게 동작합니다.
가비지컬렉션 여부는 TObjectPtr이 아닌 UPROPERTY가 담당하지요.
그래서 에디터가 아닌 실제 게임에서의 동작은 아래 두 가지 선언 모두 동일하다고 생각하시면 됩니다.

UPROPERTY()
TObjectPtr<AActor> Actor1;

UPROPERTY()
AActor* Actor2;

언리얼 오브젝트 포인터를 멤버 변수로 선언할 때 두 가지 경우만 감안해주시면 될텐데요,
첫 번째는 해당 객체가 회수안되도록 명시적으로 내가 사용하고 있다고 GC에 알려주는 겁니다. 이는 레퍼런스 카운팅을 올리면 되는데, 위 코드처럼 UPROPERTY와 UObject 포인터 선언한 멤버 변수에 값이 들어가면 이 때부터는 해당 객체는 GC에 들어가지 않습니다.
두 번째는 해당 객체를 그냥 지켜만 보는 겁니다. 다시 말하면 나는 사용할거지만 해당 객체는 소멸되도 상관없다고 알려주는 겁니다. 이 때는 아래 코드처럼 약 참조인 TWeakObjectPtr<T> 를 사용하면 됩니다.

UPROPERTY()
TWeakObjectPtr<AActor> Actor3;

약 참조 포인터를 사용하는 경우, 저장한 액터가 유효한지에 대해서는, 사용하는 사람이 스스로 감지해야 합니다. 그래서 Get함수를 사용해 가져온 후 추가로 널체크를 해줘야 하는 이중 작업이 필요합니다.

게임 로직과 무관한데 게임 콘텐츠 정보를 가져와야하는 UI 에서 이러한 약참조를 많이 사용합니다.
언급주신 게임 인스턴스는 게임 어플리케이션이 끝날때까지 살아있기 때문에, 딱히 레퍼런스 카운팅 여부를 신경쓰지않고 사용하면 됩니다. 게임 어플리케이션이 종료되기 전에 콘텐츠에 관련된 모든 언리얼 오브젝트들이 먼저 사라질테니깐요.

0

안녕하세요, 인프런 AI 인턴입니다. 가루님.

TObjectPtr와 일반 포인터의 선택은 객체 관리의 용도와 메모리 안전성을 어떻게 확보할지를 결정하는 문제입니다. TObjectPtr는 언리얼 엔진 5에서 도입된 것으로, 기존의 원시 포인터와 달리 UObject 포인터의 유효성을 체크하는 데 용이합니다. 이는 댕글링 포인터 문제를 방지하고, 가비지 컬렉터가 메모리를 더 안전하게 관리하도록 돕는 역할을 수행합니다.

질문하신 내용과 관련해서는, 객체의 생성 및 소유권에 따라 객체 관리 방식을 선택할 수 있습니다:

  1. 캐싱 목적의 포인터 사용: 객체를 직접 생성한 클래스가 아니고, 단순히 다른 객체에 대한 참조나 캐싱 용도의 포인터라면 일반 포인터(UObject*)를 사용해도 무방합니다. 이는 TObjectPtr의 추가 기능(유효성 체크 등)을 필요로 하지 않는 상황이기 때문입니다. 또한, 성능 효율성을 고려하여 잘못된 메모리 접근을 예방하는 데 큰 역할을 하지 않는다면 단순 원시 포인터 사용이 합리적일 수 있습니다.

  2. TObjectPtr 사용을 고려해야 할 경우: 만약에 해당 포인터가 메모리 안전성에 중요하다면, TObjectPtr를 사용하여 유효성을 보장하는 것이 좋습니다. 예를 들어, 여러 객체가 동시에 접근하여 생길 수 있는 문제, 즉 포인터가 올바르지 않게 될 가능성이 클 경우 TObjectPtr가 적절합니다.

이와 관련하여 자세한 내용은 아래의 기존 답변을 참조하시면, 더 깊은 이해에 도움이 될 것입니다.

요약하자면, 객체 관리와 포인터 유효성 체크가 중요한 상황에서는 TObjectPtr를, 단순 캐싱 목적이라면 일반 포인터를 사용하는 것이 보편적인 접근 방식입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

가루님의 프로필 이미지
가루

작성한 질문수

질문하기