Delegate 分析

UE 的 Delegate 分了几个不同的种类,普通的代理是模板类,Dynamic Delegate是通过宏展开生成类,Dynamic Mulitcast Delegate需要 UHT 参与生成代码,所以动态多播代理只能写到包含 generated.h 的文件中。

Delegate

  • DECLARE_DELEGATE:普通代理,可以被值传递,本质实现是 TBaseDelegare<__VA_ARGS__> 的对象,可以使用 BindUObject 等函数。

TBaseDelegate里定义了很多的辅助函数,如 BindSP/BindRaw/BindStatic/CreateSP 等。

Dynamic Delegate

  • DECLARE_DYNAMIC_DELEGATE:动态代理可以序列化,其函数可按命名查找,执行速度比常规代理慢。

动态代理本质上是继承自 TBaseDynamicDelegate 的类,TBaseDynamicDelegate又继承自TScriptDelegate,所以动态代理可以绑定 UObject 和绑定 UFUNCTION。

其中 BindUFunctionTScriptInterface的函数,而 BindUbject 是个宏,定义在 Delegate.h 中。

代码分析:

1
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnTestDynamicDelegate, const FString&, InStr);

DECLARE_DYNAMIC_DELEGATE_OneParam的宏定义为:

1
#define DECLARE_DYNAMIC_DELEGATE_OneParam(DelegateName, Param1Type, Param1Name) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE(FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, FUNC_CONCAT( Param1Type InParam1), FUNC_CONCAT(*this, InParam1), void, Param1Type )

BODY_MACRO_COMBINE宏其经过 UHT 之后生成的代码为:

1
2
3
4
5
6
7
8
9
10
11
#define GWorldClient_Plugins_HotPackage_HotPatcher_Source_HotPatcherEditor_Private_CreatePatch_ExportPatchSettings_h_22_DELEGATE \
struct _Script_HotPatcherEditor_eventOnTestDynamicDelegate_Parms \
{ \
FString InStr; \
}; \
static inline void FOnTestDynamicDelegate_DelegateWrapper(const FScriptDelegate& OnTestDynamicDelegate, const FString& InStr) \
{ \
_Script_HotPatcherEditor_eventOnTestDynamicDelegate_Parms Parms; \
Parms.InStr=InStr; \
OnTestDynamicDelegate.ProcessDelegate<UObject>(&Parms); \
}

进行展开之后,可以看到使用 DECLARE_DYNAMIC_DELEGATE_OneParam 声明的代理实际上是就被定义了一个 static 函数。

FUNC_DECLARE_DYNAMIC_DELEGATE宏是裹了一个TBaseDynamicDelegate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/** Declare user's dynamic delegate, with wrapper proxy method for executing the delegate */
#define FUNC_DECLARE_DYNAMIC_DELEGATE(TWeakPtr, DynamicDelegateName, ExecFunction, FuncParamList, FuncParamPassThru, ...) \
class DynamicDelegateName : public TBaseDynamicDelegate<TWeakPtr, __VA_ARGS__> \
{ \
public: \
/** Default constructor */ \
DynamicDelegateName() \
{ \
} \
\
/** Construction from an FScriptDelegate must be explicit. This is really only used by UObject system internals. */ \
explicit DynamicDelegateName(const TScriptDelegate<>& InScriptDelegate ) \
: TBaseDynamicDelegate<TWeakPtr, __VA_ARGS__>(InScriptDelegate) \
{ \
} \
\
/** Execute the delegate. If the function pointer is not valid, an error will occur. */ \
inline void Execute(FuncParamList) const \
{ \
/* Verify that the user object is still valid. We only have a weak reference to it. */ \
checkSlow(IsBound() ); \
ExecFunction(FuncParamPassThru); \
} \
/** Execute the delegate, but only if the function pointer is still valid */ \
inline bool ExecuteIfBound(FuncParamList) const \
{ \
if(IsBound() ) \
{ \
ExecFunction(FuncParamPassThru); \
return true; \
} \
return false; \
} \
};

所有的宏都被展开之后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct _Script_HotPatcherEditor_eventOnTestDynamicDelegate_Parms
{
FString InStr;
};
static inline void FOnTestDynamicDelegate_DelegateWrapper(const FScriptDelegate& OnTestDynamicDelegate, const FString& InStr)
{
_Script_HotPatcherEditor_eventOnTestDynamicDelegate_Parms Parms;
Parms.InStr=InStr;
OnTestDynamicDelegate.ProcessDelegate<UObject>(&Parms);
}


class FOnTestDynamicDelegate : public TBaseDynamicDelegate<FWeakObjectPtr, void, const FString&>
{
public:
FOnTestDynamicDelegate() { }
explicit FOnTestDynamicDelegate(const TScriptDelegate<>& InScriptDelegate ) : TBaseDynamicDelegate<FWeakObjectPtr, void, const FString&>(InScriptDelegate) { }
inline void Execute(const FString& InStr ) const { checkSlow(IsBound()); FOnTestDynamicDelegate_DelegateWrapper(*this, InStr ); }
inline bool ExecuteIfBound(const FString& InStr ) const
{
if(IsBound())
{
FOnTestDynamicDelegate_DelegateWrapper(*this, InStr );
return true;
}
return false;
}
};

在使用的时候可以通过 BindUFunction 来绑定函数,通过 Execute 或者 ExecuteIfBound 来调用。

在当作回调函数传递的时候比较方便,因为它继承自FScriptDelegate,可以当作通用的方式传递。

Multicast Delegate

  • 多播代理 :与普通代理的大部分功能相同,它们只拥有对象的弱引用,可以和结构体一起使用,可以复制。其本质是TMulticastDelegate<__VA_ARGS__> 的对象。多播代理可以被加载 / 保存和远程触发,但多播代理不能使用返回值。

多播代理可以具有多个绑定,当 Broadcast 触发时,所有的绑定都会被调用。

多播代理可以使用 Add/AddStatic/AddRaw/AddSP/AddUObject/Remove/RemoveAll 等函数。

注意:RemoveAll 会删除所有绑定时提供指针的代理,未绑定到对象的 Raw 代理不会被该函数删除。

Dynamic Multicast Delegate

  • DECLARE_DYNAMIC_MULITCAST_DELEGATE:动态多播代理。

动态多播代理必须要绑定到 UFUNCTION 的函数,使用宏 AddDynamic 或者 AddUnique 来添加绑定。

代码分析:

1
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTestDynamicMultiDelegate, const FString&, InStr);

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam其宏定义为:

1
2
3
#define DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(DelegateName, Param1Type, Param1Name)\
BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE)\
FUNC_DECLARE_DYNAMIC_MULTICAST_DELEGATE(FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, FUNC_CONCAT( Param1Type InParam1), FUNC_CONCAT(*this, InParam1), void, Param1Type )

其中 BODY_MACRO_COMBINE 经过 UHT 之后生成下列代码:

1
2
3
4
5
6
7
8
9
10
11
#define GWorldClient_Plugins_HotPackage_HotPatcher_Source_HotPatcherEditor_Private_CreatePatch_ExportPatchSettings_h_22_DELEGATE \
struct _Script_HotPatcherEditor_eventOnTestDynamicMultiDelegate_Parms \
{ \
FString InStr; \
}; \
static inline void FOnTestDynamicMultiDelegate_DelegateWrapper(const FMulticastScriptDelegate& OnTestDynamicMultiDelegate, const FString& InStr) \
{ \
_Script_HotPatcherEditor_eventOnTestDynamicMultiDelegate_Parms Parms; \
Parms.InStr=InStr; \
OnTestDynamicMultiDelegate.ProcessMulticastDelegate<UObject>(&Parms); \
}

FUNC_DECLARE_DYNAMIC_MULTICAST_DELEGATE则创建了一个继承自 TBaseDynamicMulticastDelegate 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** Declare user's dynamic multi-cast delegate, with wrapper proxy method for executing the delegate */
#define FUNC_DECLARE_DYNAMIC_MULTICAST_DELEGATE(TWeakPtr, DynamicMulticastDelegateName, ExecFunction, FuncParamList, FuncParamPassThru, ...) \
class DynamicMulticastDelegateName : public TBaseDynamicMulticastDelegate<TWeakPtr, __VA_ARGS__> \
{ \
public: \
/** Default constructor */ \
DynamicMulticastDelegateName() \
{ \
} \
\
/** Construction from an FMulticastScriptDelegate must be explicit. This is really only used by UObject system internals. */ \
explicit DynamicMulticastDelegateName(const TMulticastScriptDelegate<>& InMulticastScriptDelegate ) \
: TBaseDynamicMulticastDelegate<TWeakPtr, __VA_ARGS__>(InMulticastScriptDelegate) \
{ \
} \
\
/** Broadcasts this delegate to all bound objects, except to those that may have expired */ \
void Broadcast(FuncParamList) const \
{ \
ExecFunction(FuncParamPassThru); \
} \
};

FUNC_CONCT宏只是简单的拼接:

1
2
/** Helper macro that enables passing comma-separated arguments as a single macro parameter */
#define FUNC_CONCAT(...) __VA_ARGS__

展开所有宏之后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct _Script_HotPatcherEditor_eventOnTestDynamicMultiDelegate_Parms
{
FString InStr;
};
static inline void FOnTestDynamicMultiDelegate_DelegateWrapper(const FMulticastScriptDelegate& OnTestDynamicMultiDelegate, const FString& InStr)
{
_Script_HotPatcherEditor_eventOnTestDynamicMultiDelegate_Parms Parms;
Parms.InStr=InStr;
OnTestDynamicMultiDelegate.ProcessMulticastDelegate<UObject>(&Parms);
}

class FOnTestDynamicMultiDelegate : public TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, const FString&>
{
public:
FOnTestDynamicMultiDelegate() { }
explicit FOnTestDynamicMultiDelegate(const TMulticastScriptDelegate<>& InMulticastScriptDelegate ) : TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, const FString&>(InMulticastScriptDelegate) { }
void Broadcast(const FString& InStr ) const { FOnTestDynamicMultiDelegate_DelegateWrapper(*this, InStr ); }
};

注,由上面的代码可知,只有普通代理(DECLARE_DELEGATE)声明的代理才可以使用 TBaseDelegateBindUObject等函数,动态代理和多播代理都不可以。