* 정의

 

일반적으로 델리게이트(delegate)는 함수를 안전하게 호출할 있는 기능을 의미하며 콜백 이벤트 리스너를 구현하는데 사용된다. 

 

언리얼 엔진에서는 바인드 있는 함수의 수에 따라 싱글, 멀티 캐스트로 구분하며 블루프린트와 연동할 있는 다이나믹 델리게이트를 추가로 지원하고 있다.

 

델리게이트는 객체의 멤버 함수에 동적으로 바인딩 되어 호출자가 객체의 유형을 알지 못해도 나중에 객체의 함수를 호출할 수 있다. 복사에 대해 안전하지만 값으로 넘겨줄 경우 힙에 추가 할당을 하게 되니 참조로 넘겨주는게 좋다.

 

 

* 동작 방식

 

- 선언

 

언리얼 엔진에서는 델리게이트 타입을 선언하기 위한 여러가지 매크로를 제공한다. 이러한 매크로는 델리게이트 타입의 이름, 반환형, 매개 변수를 포함하고 있으며 상황에 따라 적절한 조합을 사용해야한다.

 

현재는 다음과 같은 것들을 제공하고 있다.

  • 값을 반환하는 함수
  • const로 선언된 함수
  • 최대 4개의 페이로드 변수
  • 최대 8개까지의 함수 매개변수

 

실제 사용하는 델리게이트 매크로는 다음과 같다.

함수 형식 델리게이트 타입 선언 매크로
void Function() DECLARE_DELEGATE(DelegateName)
void Function(Param1) DECLARE_DELEGATE_OneParam(DelegateName, Param1Type)
void Function(Param1, Param2) DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type)
void Function(Param1, Param2, ...) DECLARE_DELEGATE_<Num>Params(DelegateName, Param1Type, Param2Type, ...)
<RetValType> Function() DECLARE_DELEGATE_RetVal(RetValType, DelegateName)
<RetValType> Function(Param1) DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type)
<RetValType> Function(Param1, Param2) DECLARE_DELEGATE_RetVal_TwoParams(RetValType, DelegateName, Param1Type, Param2Type)
<RetValType> Function(Param1, Param2, ...) DECLARE_DELEGATE_RetVal_<Num>Params(RetValType, DelegateName, Param1Type, Param2Type, ...)

 

사용 예시는 다음과 같다.

UDELEGATE(BlueprintAuthorityOnly)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FInstigatedAnyDamageSignature, float, Damage, const UDamageType*, DamageType, AActor*, DamagedActor, AActor*, DamageCauser);

 

 

Dynamic, Multi-Cast 형식의 델리게이트가 존재하며 이에 대한 설명은 다음과 같다.

https://create-new-worlds.tistory.com/174

 

[UnrealEngine] Dynamic Delegate, Multicast Delegate

* Dynamic Delegate - 정의 다이나믹 델리게이트는 직렬화될 수 있고 리플렉션을 지원하는 델리게이트이다. 함수들을 이름으로 찾기 때문에 일반적인 델리게이트보다 느리다. - 선언 일반적인 델리게

create-new-worlds.tistory.com

 

 

- 바인딩

 

델리게이트 시스템은 특정 객체 타입에 대해 이해하고 있으며 이를 관리할 때 추가 처리를 해준다.

만약 UObject나 SharedPtr의 멤버에 델리게이트를 바인딩하면 델리게이트는 이를 약 참조(weak reference)로 유지한다. 만약 이러한 객체가 파괴되면 IsBound나 ExecuteIfBound 함수를 호출하여 이를 처리할 수 있다.

 

바인딩 함수는 다음과 같다.

함수 설명
Bind 존재하는 델리게이트 객체에 바인딩한다.
BindStatic 전역 생 함수 포인터를 바인딩한다.
BindRaw 포인터를 바인딩한다. 생 포인터는 어떤 참조도 사용하지 않기 때문에 삭제된 객체에 대해 Execute나 ExcuteIfBound를 안전하게 사용할 수 없다.
BindLambda 주로 람다 함수를 바인딩한다.
BindSP 공유 포인터 기반 멤버 함수를 바인딩한다. 공유 포인터 델리게이트는 바인딩한 객체를 약 참조로 관리한다. ExcuteIfBound 함수를 통해 안전하게 사용할 수 있다.
BindUObject UObject 멤버 함수를 바인딩한다. UObject 델리게이트는 해당 UObject를 약 참조로 관리한다. ExcuteIfBound 함수를 통해 안전하게 사용할 수 있다.
UnBind 언바인딩한다.

 

 

- 페이로드

// 페이로드라는 용어를 사용한 이유는 바로 함수 호출에 사용되지 않고 저장되어 실제 함수 호출 때 함수 인자로 전송되기 때문인 것 같다. 페이로드라는 용어로 보아 직렬화된 데이터를 델리게이트 내부에 유지하지 않을까 싶다.

델리게이트에 바인딩할 때 넘겨주는 데이터를 의미하며 바인딩된 함수가 호출될 때 해당 함수로 직접 전달된다. 페이로드는 델리게이트 내부에 저장되며 Dynamic을 제외한 모든 델리게이트에서 자동으로 페이로드 변수를 지원한다. 이러한 추가 인수들은 반드시 델리게이트의 매개변수 인수 뒤에 위치해야한다.

 

 

- 실행

 

델리게이트에 바인딩된 함수는 Execute라는 함수를 통해 호출된다. 실행하기 전에 무조건 델리게이트가 바인딩 되었는 지 확인 해야한다. 이와 같은 방식으로 실행하면 특히 델리게이트에 초기화되지 않고 접근되는 반환값, 출력 매개변수가 있는 경우 더욱 안전하게 처리할 수 있다.

 

바인딩되지 않은 델리게이트를 수행하면 메모리를 망칠 수 있기 때문에 IsBound 함수를 호출하여 체크 후에 안전하기 Execute를 호출한다. 만약 반환값이 없다면 ExecuteIfBound를 사용할 수 있다.

실행 함수 설명
Execute 바인딩 체크를 하지 않고 델리게이트를 실행한다.
ExecuteIfBound 델리게이트가 바인딩되었는 지 확인하고 Execute함수를 호출한다. 
IsBound 델리게이트의 바인딩 여부를 체크한다. 보통 Execute함수 전에 호출한다.

 

+ Recent posts