언리얼 엔진에서 참조 카운트 및 메모리 관리를 받으려면 직접 malloc/free, 또는 new/delete로 생성과 삭제를 하면 안 된다. 자동으로 이뤄지는 메모리 관리를 받기 위해서는 모든 오브젝트의 메모리 할당은 NewObject<>()
또는 SpawnActor<>()
함수를 사용해야 한다.
NewObject
함수 사용해서 UObject 파생 모델을 인스턴스화한다. 이 함수는 C++ 클래스 이름만 취하는 것이 아니라 C++ 클래스의 블루프린트 클래스 파생 모델(UClass* 참조)도 필요로 한다. UClass* 참조는 블루프린트에 대한 포인터다.
C++ 코드에서 특정 블루프린트의 인스턴스를 인스턴스화하려면 이름을 알아야 하는데 이 이름은 컴파일 후에만 접근할 수 있기 때문에 이름을 알려줄 방법이 필요함. 이를 위해 TSubclassOf
타입의 변수를 사용. 코드 수준에서 하드코딩하지 않을 수 있다. 특정 리소스의 이름을 코드에서 직접 입력하는 것을 원치 않듯이, 파생된 블루프린트 클래스 이름을 하드코딩하는 것도 원치 않는 방식이다.
또는 FStringClassReference
(UE4) 멤버를 사용해 처리할 수 있다. UE5에서는 FSoftClassPath
임.
둘은 같은 역할을 함.
AActor 클래스 파생 - GetWorld()->SpawnActor<> 사용
UObject 파생 클래스 - NewObject<> 사용
NewObject 함수는 객체지향 프로그래밍에서 '팩토리'라고 부르는 디자인 패턴이라고 한다. 오브젝트를 직접 생성하는 대신 팩토리에 원하는 오브젝트의 생성을 요청하는 방식. 팩토리 패턴을 잘 사용하면 생성되는 오브젝트를 추적하는 일이 가능해진다.
메모리 관리 기능의 도움을 받으면 메모리 해체를 잊는 실수를 걱정하지 않아도 된다. 오브젝트를 참조하는 포인터의 수를 기억한다. 참조하는 포인터가 없을 때는 자동으로 즉시 지워지거나, 표시한 후에 다음 차례의 가비지 컬렉션 때 지워진다.
참조 카운트가 0으로 떨어지기 전에 ConditionalBeginDestroy 함수 사용해서 수동으로 메모리에서 해제할 수 있다. ConditionalBeginDestroy() 명령은 메모리 해제 절차를 시작하며, 재정의 가능한 BeginDestroy()와 FinishDestroy() 함수를 호출한다.
(근데 플젝할 때 ConditionalBeginDestroy함수를 사용한 적이 없다... Actor는 Destroy로 삭제하는 부분이 꽤 있다. 발사체, 스포너 등등.)
>> TSharedPtr, TWeakPtr, TAutoPtr
delete 호출을 잊는 것을 걱정해서 사용하는 스마트 포인터.
C++ 코드에서 수동으로 UObject 파생이 아닌 오브젝트를 추적하고 삭제하는 상황이라면 TSharedPtr, TSharedRef와 같은 스마트 포인터를 사용. new 키워드를 사용해 동적으로 할당되는 오브젝트를 사용한다면, 이를 참조 카운트를 지원하는 포인터로 감싸서 자동으로 메모리 해제가 일어나도록 할 수 있다.
TSharedPtr 은 모든 커스텀 C++ 오브젝트를 참조 카운트 방식으로 만든다. (UObject는 이미 참조 카운트 방식임. 그 파생 클래스는 TSharedPtr 사용 불가능) 공유 오브젝트에 더 이상 참조가 없을 때 할당 해제된다. 스레드로부터 안전한(참조 카운팅 방식이 atomic하다고 함. 카운트가 0이되어 제거하는 행동을 정확히 한 번만 실행.-> 기본 오브젝트를 별도의 스레드에서 안전하게 조작할 수 있음을 의미.) 참조 카운트 포인터 타입.
TWeakPtr도 참조 카운트 오브젝트를 지원하는데 삭제를 방지할 수 없다는 특징이 있음.
TAutoPtr 스레드로부터 안전하지 않은 공유 포인터.
UCLASS의 멤버로 UPROPERTY 같은 오브젝트가 있을 때 블루프린트에서 편집하지 않더라도 UPROPERTY()로 선언해야 한다. 그렇지 않으면 제대로 할당되지 않는다.