언리얼 컨테이너 라이브러리 Ⅱ - 구조체와 Map
UStruct
데이터 저장/전송에 특화된 가벼운 객체
- UStruct를 쓰자/쓰지말자는 논의가 있음
- UObject와 달리 가비지 컬렉션 대상이 아님
- 함수도 가질 수 없음
사용방법
USTRUCT(지정자)매크로로 리플렉션 시스템에 등록GENERATED_BODY()로 리플렉션 코드 자동생성
구조체 지정자
- BlueprintType: 블루프린트 변수 타입으로 사용 가능
- Atomic: 멤버 변수에 대해 멀티스레드 환경에서 안전한 동시 접근 보장
- NoExport: 자동 생성 코드 제외, 메타데이터 파싱 전용(블루프린트나 런타임 노출 x)
STL struct와 차이점
- STL struct는 언리얼 에디터나 블루프린트에서 인식 못함
- 직렬화, 리플렉션, 가비지 컬렉터 기능 못씀
언리얼 엔진의 메모리관리
C++ 언어 메모리 관리의 문제점
- 저수준 언어이므로 메모리 주소에 직접 접근하는 포인터를 사용해 오브젝트를 관리한다
- 그러다보니 프로그래머가 직접 할당(new)과 해지(delete) 짝 맞추기를 해야 한다. 이는 휴먼 에러 발생 가능성을 높인다
- 잘못된 포인터 사용 예시
- 메모리 누수(Leak): delete를 깜빡해 힙에 메모리가 그대로 남아있음
- 허상 포인터(dangling): 이미 해제해 무효화된 오브젝트의 주소를 가리키는 포인터
- 와일드 포인터(wild): 값이 초기화되지 않아 엉뚱한 주소를 가리키는 포인터(직접 nullptr로 초기화해ㅑ함)
- 잘못된 포인터 값은 다양한 문제를 일으키며, 한 번의 실수는 크래시로 이어짐
- 프로그램 규모가 커지고 복잡해질수록 실수활 확률도 같이 증가함
가비지 컬렉션 시스템
- 프로그램에서 더 이상 사용되지 않는 오브젝트를 자동으로 감지해 메모리를 회수하는 시스템
- UObject는 참조타입임. 참조타입은 레퍼런스로, 힙에서 사용됨을 뜻함. 결국 힙에 올라가있는 리소스를 파악하는 것.
- 동적으로 생성된 모든 오브젝트 정보를 모아둔 저장소를 사용해 추적함
- Mark-Sweep 방식
- 저장소에서 최초 검색을 시작하는 루트 오브젝트를 표기
- 루트 오브젝트가 참조하는 객체를 찾아 Marking
- Mark된 객체로부터 다시 참조하는 객체를 찾아 Mark하고 계속 반복
- 끝나면 저장소에는 Marked 객체외 Unmarked 객체 두 그룹으로 나뉜다
- 마크되지 않은 객체들의 메모리를 회수한다(Sweep)
언리얼의 가비지 컬렉션 시스템
- 언리얼이 자체적으로 구축함
- 지정된 주기마다 몰아서 없애도록 설정돼있음(기본값 60초)
- 성능 향상을 위한 벙렬 처리, 클러스터링 기능을 탑재함
객체 저장소
- 관리되는 모든 언리얼 오브젝트의 정보를 저장하는 전역변수:
GUObjectArray GUObjectArray의 각 요소에는 Flag가 설정돼있음- Garbage flag: 참조가 없어 회수 예정인 오브젝트
- RootSet flag: 참조가 없어도 회수하지 않는 특별한 오브젝트
메모리 회수
- 가비지 컬렉터는 지정된 시간에 따라 주기적으로 메모리를 회수한다.
- Garbage flag로 설정된 오브젝트를 파악하고 메모리를 안전하게 회수함
- Garbage flag는 수동으로 설정하는게 아니고 시스템이 알아서 설정함

회수되지 않는 언리얼 오브젝트
- 언리얼 엔진 방식으로 참조를 설정한 언리얼 오브젝트
UPROPERTY()로 참조된 언리얼 오브젝트(대부분의 경우 이를 사용)AddReferencedObject()함수를 통해 참조를 설정한 언리얼 오브젝트
- 루트셋으로 지정된 언리얼 오브젝트
핵심: 오브젝트 포인터는 가급적 UPROPERTY()로 선언하고, 메모리는 가비지컬렉터가 자동으로 관리하도록 위임한다.
일반 클래스에서 언리얼 오브젝트를 관리하는 경우
UPROPERTY()를 사용하지 못하는 일반 C++ 클래스가 언리얼 오브젝트를 관리해야할 때FGCObject클래스를 상속받은 후AddReferencedObjects()함수를 구현한다.- 함수 구현 부에서 관리할 언리얼 오브젝트를 추가해줌
언리얼 오브젝트의 관리 원칙
- 언리얼 객체 내 언리얼 객체:
UPROPERTY()사용 - 일반 C++ 객체 내 언리얼 객체:
FGCObject상속 후 구현 - 생성된 언리얼 객체는 강제로 지우려하지 말 것
- 참조를 끊는다는 생각으로 설계할 것
- 가비지컬렉터에게 회수를 재촉할 수는 있으나 보장은 못받음(
ForceGarbageCollerction()) - 콘텐츠 제작에서
Destroy()함수를 사용할 수는 있으나 내부적으로는 위와 동일함