赞
踩
本文介绍了Delegate的使用方法,内容我认为比较全面,认真读完绝对会有收获,但并没有对其实现原理进行深入剖析,读者可以查阅这些文章对Delegate的原理进行深入了解
UPROPERTY()
//无返回值,无函数参数
DECLARE_DELEGATE(FTestDelegate);
//有返回值,无函数参数
DECLARE_DELEGATE_RetVal(int32,FTestDelegate);
//无返回值,1个函数参数
DECLARE_DELEGATE_OneParam(FTestDelegate, int32);
//有返回值,1个函数参数
DECLARE_DELEGATE_RetVal_OneParam(bool, FTestDelegate, int32);
注意事项:
声明和定义委托及代理对象
DECLARE_DELEGATE_RetVal_OneParam(bool, FTestDelegate, int32);
FTestDelegate MyTestDelegate;
作用:绑定类的static函数,全局函数
//静态函数
bool ADelegateTest::StaticTest(int32 num)
{
UE_LOG(LogTemp, Warning, TEXT("Num:%d"), num);
return true;
}
//绑定和执行
MyTestDelegate.BindStatic(&ThisClass::StaticTest);
bool RetValue = false;
if (MyTestDelegate.IsBound())
{
RetValue = MyTestDelegate.Execute(10);
}
UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
注意拥有返回值的Delegate无法使用ExecuteIfBound
,需要手动调用IsBound
来检测有效性,不然有可能会触发内存违规操作
这里再解释payload参数的意思,后续不会再举例子
//静态函数
bool ADelegateTest::StaticTestPayload(int32 num, FString text)
{
UE_LOG(LogTemp, Warning, TEXT("Num:%d text:%s"), num, *text);
return true;
}
//绑定和执行
MyTestDelegate.BindStatic(&ThisClass::StaticTestPayload, FString("BindStatic PatloadTest"));
bool RetValue = false;
if (MyTestDelegate.IsBound())
{
RetValue = MyTestDelegate.Execute(10);
}
UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
作用:绑定普通C++对象的成员函数
//普通C++对象 class DelegateTestClass { public: bool BindRawTest(int32 num, FString text) { UE_LOG(LogTemp, Warning, TEXT("Num:%d text:%s"), num, *text); return true; } }; //绑定和执行 DelegateTestClass TestObject; MyTestDelegate.BindRaw(&TestObject, &DelegateTestClass::BindRawTest,FString("BindRawTest")); bool RetValue = false; if (MyTestDelegate.IsBound()) { RetValue = MyTestDelegate.Execute(10); } UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
注意事项:
IsBound
或者 ExecuteIfBound
来检测出来,只有通过if(object)来阻止若将代码改造成下面的样子,则会出现内存违规,text后面会跟着一串乱码
//普通C++对象 class DelegateTestClass { FString text = "DelegateTestClass"; public: bool BindRawTest(int32 num) { UE_LOG(LogTemp, Warning, TEXT("Num:%d text:%s"), num, *text); return true; } }; //绑定和执行 DelegateTestClass* TestObject = new DelegateTestClass(); MyTestDelegate.BindRaw(TestObject, &DelegateTestClass::BindRawTest); delete TestObject; bool RetValue = false; if (MyTestDelegate.IsBound()) { RetValue = MyTestDelegate.Execute(10); } UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
作用:绑定Lambda函数(可捕获参数)
MyTestDelegate.BindLambda(
[](int num) -> bool
{
UE_LOG(LogTemp, Warning, TEXT("Num:%d"), num);
return true;
}
);
bool RetValue = false;
if (MyTestDelegate.IsBound())
{
RetValue = MyTestDelegate.Execute(50);
}
UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
注意事项: 若lambda表达式捕获外部变量已被销毁,触发代理执行lambda表达式,将会导致内存违规操作。和BindRaw同理无法通过 IsBound
或者 ExecuteIfBound
来避免
作用:绑定Lambda函数(可捕获参数),但是会将其与一个UObject对象进行弱引用关联(不影响该对象被gc回收),若这个UObject对象被gc回收,执行调用IsBound
或ExecuteIfBound
会检测到该UObject无效,则可避免内存违规操作
ADelegateTest* UObjectTest = NewObject<ADelegateTest>(this, ADelegateTest::StaticClass()); MyTestDelegate.BindWeakLambda( UObjectTest, [](int num) -> bool { UE_LOG(LogTemp, Warning, TEXT("Num:%d"), num); return true; } ); //UObjectTest->Destroy(); 如果UObject被销毁的话,不会打印num的数值 bool RetValue = false; if (MyTestDelegate.IsBound()) { RetValue = MyTestDelegate.Execute(50); } UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
作用:绑定UObject的成员函数,执行调用IsBound
或ExecuteIfBound
会检测该UObject的有效性,避免内存违规操作
//成员函数
bool ADelegateTest::PrintTest(int32 num)
{
UE_LOG(LogTemp, Warning, TEXT("Num:%d"), num);
return true;
}
//绑定和执行
MyTestDelegate.BindUObject(this, &ADelegateTest::PrintTest);
bool RetValue = false;
if (MyTestDelegate.IsBound())
{
RetValue = MyTestDelegate.Execute(50);
}
UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
这个成员函数加不加UFUNCTION()
都可以绑定
作用:绑定UObject的UFUNCTION()成员函数,执行调用IsBound
或ExecuteIfBound
会检测该UObject的有效性,避免内存违规操作
BindUFunction需要用到UE4的反射机制,因此回调函数需要用UFUNCTION()宏包住,否则UE无法通过FunctionName查找到对应的函数,会导致绑定失败
//函数定义 UFUNCTION() bool PrintTestUFunction(int32 num) { UE_LOG(LogTemp, Warning, TEXT("PrintTestUFunction Num:%d"), num); return true; } //绑定和执行 MyTestDelegate.BindUFunction(this, FName("PrintTestUFunction")); bool RetValue = false; if (MyTestDelegate.IsBound()) { RetValue = MyTestDelegate.Execute(50); } UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
注意事项: 请注意FunctionName的内容格式
FName("PrintTestUFunction")
STATIC_FUNCTION_FNAME(TEXT("ADelegateTest::PrintTestUFunction"))
FName("ADelegateTest::PrintTestUFunction")
格外小心FunctionName的内容是否正确,错误的格式或者没加 UFUNCTION() 宏都会使编辑器崩溃
作用:绑定被UE共享引用所引用的普通C++对象的成员函数,解决BindRow可能触发的内存违规操作
我们这里新建一个函数用于测试Delegate绑定函数的有效性
UFUNCTION(BlueprintCallable)
void PrintAndTestDelegate(int32 num)
{
bool RetValue = false;
if (MyTestDelegate.IsBound())
{
RetValue = MyTestDelegate.Execute(num);
}
UE_LOG(LogTemp, Warning, TEXT("RetValue:%d"), RetValue);
}
在BeginPlay()中绑定执行Delegate并测试
void ADelegateTest::BeginPlay()
{
Super::BeginPlay();
TSharedRef<DelegateTestClass> objectSP = MakeShareable(new DelegateTestClass());
MyTestDelegate.BindSP(objectSP, &DelegateTestClass::BindSPTest);
PrintAndTestDelegate(10);
}
开始会调用一次PrintAndTestDelegate
,然后等进入游戏之后,我们手动调用一次PrintAndTestDelegate
这里利用了UE 共享引用的功能,当执行完毕BeginPlay
后,objectSP所引用的对象会被销毁,当第二次调用PrintAndTestDelegate
理应得到 RetValue:0,说明BindSP
可以执行调用IsBound
或ExecuteIfBound
检测引用对象的有效性,避免内存违规操作
与BindSP相比,只是使用了线程安全的共享引用而已
TSharedRef<DelegateTestClass, ESPMode::ThreadSafe> objectSP = MakeShareable(new DelegateTestClass());
MyTestDelegate.BindThreadSafeSP(objectSP, &DelegateTestClass::BindSPTest);
PrintAndTestDelegate(10);
MyTestDelegate.Unbind();
还存在一系列CreateThreadSafeSP
、CreateStatic
等,它们的使用方法和BindXXX大致相同。区别在于CreateXXX是static函数,且返回了一个Delegate,而不是直接赋值给 *this
可以观察BindXXX的原理,就是调用了CreateXXX罢了
template <typename UserClass, typename... VarTypes>
inline void BindThreadSafeSP(const TSharedRef<UserClass, ESPMode::ThreadSafe>& InUserObjectRef, typename TMemFunPtrType<false, UserClass, RetValType (ParamTypes..., VarTypes...)>::Type InFunc, VarTypes... Vars)
{
static_assert(!TIsConst<UserClass>::Value, "Attempting to bind a delegate with a const object pointer and non-const member function.");
*this = CreateThreadSafeSP(InUserObjectRef, InFunc, Vars...);
}
若使用CreateXXX,这样做就等效于MyTestDelegate.BindThreadSafeSP(objectSP, &DelegateTestClass::BindSPTest)
MyTestDelegate = FTestDelegate::CreateThreadSafeSP(objectSP, &DelegateTestClass::BindSPTest);
UPROPERTY()
ParamType后面没有ParamName,不然会报错 (动态委托ParamType后面必须跟着ParamName)
//无函数参数
DECLARE_MULTICAST_DELEGATE(FTestDelegate);
//2个函数参数
DECLARE_MULTICAST_DELEGATE_TwoParams(FTestDelegate, int32, FString);
声明和定义委托及代理对象
DECLARE_MULTICAST_DELEGATE_TwoParams(FTestMulDelegate, int32, FString);
FTestMulDelegate MyMulDelegate;
使用方法和单播十分相似,下面列出区别点
Broadcast
BindXXX
----> AddXXX
Broadcast
会自动检测绑定的UObject或智能引用对象的有效性,再来执行代理。但像AddRaw
,AddLambda
这类绑定方式还是可能触发内存违规操作新提供了一个Add
API,查看其源码
FDelegateHandle Add(FDelegate&& InNewDelegate)
{
FDelegateHandle Result;
if (Super::GetDelegateInstanceProtectedHelper(InNewDelegate))
{
Result = Super::AddDelegateInstance(MoveTemp(InNewDelegate));
}
return Result;
}
发现需要传递一个FDelegate
,继续跟踪其定义
/** Type definition for unicast delegate classes whose delegate instances are compatible with this delegate. */
using FDelegate = TDelegate<void(ParamTypes...), UserPolicy>;
发现FDelegate
就是一个参数类型和这个多播相同的一个单播,从而推测出多播就是保存了多个单播,进而实现了可以绑定多个函数指针
而Add的使用方法就变得很简单了
//定义静态函数
static void StaticTestMul(int32 num, FString text)
{
UE_LOG(LogTemp, Warning, TEXT("StaticTestMul Num:%d text:%s"), num, *text);
}
//定义一个单播代理对象
FTestMulDelegate::FDelegate TempDelegate = FTestMulDelegate::FDelegate::CreateStatic(&ADelegateTest::StaticTestMul);
//Add
MyMulDelegate.Add(TempDelegate);
MyMulDelegate.Broadcast(20, FString("MyMulDelegateTest"));
查看AddXXX
源码发现,就是通过Add
来实现的
template <typename... VarTypes>
inline FDelegateHandle AddStatic(typename TBaseStaticDelegateInstance<void (ParamTypes...), UserPolicy, VarTypes...>::FFuncPtr InFunc, VarTypes... Vars)
{
return Add(FDelegate::CreateStatic(InFunc, Vars...));
}
多播的AddXXX包括Add都会返回一个FDelegateHandle
,这是一个句柄用于识别绑定在多播上的对象或者函数,可以通过它来解除绑定
作用:解除FDelegateHandle
对应的函数绑定
FTestMulDelegate::FDelegate TempDelegate = FTestMulDelegate::FDelegate::CreateStatic(&ADelegateTest::StaticTestMul);
FDelegateHandle StaticHandle = MyMulDelegate.Add(TempDelegate);
MyMulDelegate.Remove(StaticHandle);
作用:解除特定UObject对象所有的绑定,注意只能是UObject,普通C++对象不会被解除,比如下面这种情况,还是会打印信息
TSharedRef<DelegateTestClass> objectSP = MakeShareable(new DelegateTestClass());
MyMulDelegate.AddSP(objectSP, &DelegateTestClass::AddSPTest);
MyMulDelegate.RemoveAll(&objectSP);
MyMulDelegate.Broadcast(100, FString("MyMulDelegateTest"));
还需要注意一个地方,请看下面一段代码,预测结果,其中ADelegateTest::StaticTestMul
是本类的一个static函数
//函数定义
static void StaticTestMul(int32 num, FString text)
{
UE_LOG(LogTemp, Warning, TEXT("StaticTestMul Num:%d text:%s"), num, *text);
}
MyMulDelegate.AddStatic(&ADelegateTest::StaticTestMul);
MyMulDelegate.RemoveAll(this);
MyMulDelegate.Broadcast(100, FString("MyMulDelegateTest"));
可能有些人会认为不会打印StaticTestMul Num:100 text:MyMulDelegateTest
,static函数也属于该UObject,会被RemoveAll
解除绑定。但是答案相反,会打印信息。类的static函数是属于类的,不是属于某个特定对象,RemoveAll不能解除类的绑定
作用:清除多播的所有绑定
MyMulDelegate..Clear();
这些API在单播中也存在,放在这里一起讲了
作用:判断是否绑定了至少一个函数指针
MyMulDelegate.IsBound()
作用:判断是否绑定了特定UObject对象的函数指针
MyMulDelegate.IsBoundToObject(this)
需要注意的是,和RemoveAll
同理,绑定类的静态函数,不认为是绑定了该类的实例对象,如下面一段代码
MyMulDelegate.AddStatic(&ADelegateTest::StaticTestMul);
bool bBoundOb = MyMulDelegate.IsBoundToObject(this);
bool bBound = MyMulDelegate.IsBound();
UE_LOG(LogTemp, Warning, TEXT("bBoundOb:%d\tbBound:%d"), bBoundOb, bBound);
会打印 bBoundOb:0 bBound:1
其实按Delegate绑定static函数的原理来说,我的解答是错误的,大家不要在意,理解意思即可
单播和多播可以统称为静态委托,它们都不支持反射以及序列化。而接下来介绍的动态单播和动态多播都是动态委托,支持反射以及序列化。动态委托主要就是集成了UObject的反射系统,让其可以注册蓝图实现的函数及支持序列化存储到本机。当然也可以绑定原生C++函数代码,前提是这个函数被UFUNCTION
标记(因为UFUNCTION标记过,这个函数就可以进行反射与序列化了)。通常情况动态委托执行速度比静态委托慢
同时需要注意动态委托不支持Payload,因此,绑定函数与代理对象的参数、返回值必须完全一致。
UPROPERTY()
,但不能BlueprintAssignable
动态委托ParamType后面必须跟着ParamName
//无返回值,无函数参数
DECLARE_DYNAMIC_DELEGATE(FTestDYDelegate);
//无返回值,2个函数参数
DECLARE_DYNAMIC_DELEGATE_TwoParams(FTestDYDelegate, int32, num, FString, text);
//有返回值,无函数参数
DECLARE_DYNAMIC_DELEGATE_RetVal(bool, FTestDYDelegate);
//有返回值,2个函数参数
DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(bool, FTestDYDelegate, int32, num, FString, text);
声明和定义委托
DECLARE_DYNAMIC_DELEGATE_RetVal_TwoParams(bool, FTestDYDelegate, int32, num, FString, text);
UPROPERTY(BlueprintReadWrite)
FTestDYDelegate MyDYDelegate;
作用:绑定UObject的UFUNCTION函数
//函数声明和定义
UFUNCTION()
bool DYnamicPrintTest(int32 num, FString text)
{
UE_LOG(LogTemp, Warning, TEXT("DYnamicPrintTest Num:%d text:%s"), num, *text);
return true;
}
MyDYDelegate.BindUFunction(this, FName("DYnamicPrintTest"));
if (MyDYDelegate.IsBound())
{
MyDYDelegate.Execute(100, FString("MyDYDelegate Execute"));
}
注意必须加UFUNCTION()
宏
作用:绑定UObject的UFUNCTION函数
BindDynamic
是UE给我们定义好的宏,就是根据函数指针解析函数名字
#define BindDynamic( UserObject, FuncName ) __Internal_BindDynamic( UserObject, FuncName, STATIC_FUNCTION_FNAME( TEXT( #FuncName ) ) )
//绑定和执行
MyDYDelegateTwo.BindDynamic(this, &ThisClass::DYnamicPrintTestTwo);
MyDYDelegate.BindDynamic(this, &ThisClass::DYnamicPrintTest);
if (MyDYDelegate.IsBound())
{
MyDYDelegate.Execute(100, FString("MyDYDelegate Execute"));
}
MyDYDelegate.Clear();//提供给Dynamic Multicast的接口
MyDYDelegate.Unbind();
Dynamic Multicast(动态多播)与Dynamic Unicast(动态单播)相比最大的差别就是可以BlueprintAssignable
,在蓝图中进行绑定和解绑操作
UPROPERTY()
,可以BlueprintAssignable
//无函数参数
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTestDYMulDelegate);
//1个函数参数
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTestDYMulDelegate, int32, num);
在介绍绑定之前,需要注意一件事情:UFUNCTION
函数被重复绑定情况
AddUObject
绑定UFUNCTION
标记的函数,最后函数会多次执行(不会报错),而一旦使用AddUFunction
编辑器会直接崩溃声明和定义委托及代理对象
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTestDYMulDelegate, int32, num);
UPROPERTY(BlueprintAssignable)
FTestDYMulDelegate MyDYMulDelegate;
作用:根据FScriptDelegate
对象添加绑定
在使用之前,首先回忆静态多播的使用方法,发现动态多播内也存在FTestDYMulDelegate::FDelegate
变量,这个变量存放的是和动态多播变量参数一样的动态单播,但是动态单播不存在CreatXXX
这种静态函数可以创建并返回一个动态单播对象实例,所以这种思路是不可行的。
我们查看Add源码,发现传入的参数也不是FTestDYMulDelegate::FDelegate
类型的变量,而是TScriptDelegate
类变量。
void Add( const TScriptDelegate<TWeakPtr>& InDelegate )
{
// First check for any objects that may have expired
CompactInvocationList();
// Add the delegate
AddInternal( InDelegate );
}
TScriptDelegate
类的默认成员有两个,一个是UObject的弱引用智能指针,一个是FunctionName即我们要绑定的UFUNCTION函数名字
,我们就可以通过它的BindUFunction绑定UObject的UFUNCTION函数了
void BindUFunction( UObject* InObject, const FName& InFunctionName )
{
Object = InObject;
FunctionName = InFunctionName;
}
所以使用Add
的示例如下:
//函数定义
UFUNCTION()
void DynamicMulPrint(int32 num)
{
UE_LOG(LogTemp, Warning, TEXT("DynamicMulPrnt Num:%d"), num);
}
TScriptDelegate<> TempDyMulDelegate;
TempDyMulDelegate.BindUFunction(this, FName("DynamicMulPrint"));
MyDYMulDelegate.Add(TempDyMulDelegate);
MyDYMulDelegate.Add(TempDyMulDelegate);//重复绑定,运行时报错
MyDYMulDelegate.Broadcast(55);
可以使用FScriptDelegate
替换TScriptDelegate<>
,UE帮我们进行了typedef TScriptDelegate<> FScriptDelegate;
注意事项:
FScriptDelegate
绑定的函数指针类型进行检查,也就是说如果FScriptDelegate
对象绑定的函数指针参数与动态多播类型不一致是不会报错的UFUNCTION
函数会导致运行时错误Add
对动态多播进行绑定,若FunctionName输入错误,会导致不确定因素发生FScriptDelegate
不存在__Internal_BindDynamic
成员,所以也不能用BindDynamic
进行绑定作用:根据FScriptDelegate
对象添加绑定,但会检测是否重复
UFUNCTION()
void DynamicMulPrint(int32 num)
{
UE_LOG(LogTemp, Warning, TEXT("DynamicMulPrnt Num:%d"), num);
}
TScriptDelegate<> TempDyMulDelegate;
TempDyMulDelegate.BindUFunction(this, FName("DynamicMulPrint"));
MyDYMulDelegate.Add(TempDyMulDelegate);
MyDYMulDelegate.AddUnique(TempDyMulDelegate);//检测到重复绑定,不执行绑定
MyDYMulDelegate.Broadcast(55);
注意事项: 使用AddUnique
,当FScriptDelegate
对象绑定的函数指针参数与动态多播类型不一致时,Broadcast
会使编辑器直接崩溃。若FScriptDelegate
对象绑定的函数指针有返回值,不会使编辑器崩溃
作用:根据函数指针和UObject添加绑定
MyDYMulDelegate.AddDynamic(this, &ThisClass::DynamicMulPrint);
MyDYMulDelegate.AddDynamic(this, &ThisClass::DynamicMulPrint);//重复绑定,运行时错误
AddDynamic
在编译时就会检测函数指针参数类型是否一致,避免出错,推荐使用(确保不重复绑定下)
作用:根据函数指针和UObject添加绑定,但会检测是否重复
MyDYMulDelegate.AddDynamic(this, &ThisClass::DynamicMulPrint);
MyDYMulDelegate.AddUniqueDynamic(this, &ThisClass::DynamicMulPrint);//检测到重复绑定,不执行绑定
动态多播的最大好处就是可以在蓝图中进行绑定和解绑:
拥有两个重载:根据FScriptDelegate对象或者FunctionName解除绑定
//通过Object和FunctionName解除绑定
void Remove( const UObject* InObject, FName InFunctionName );
//通过FScriptDelegate对象解除绑定
void Remove( const TScriptDelegate<TWeakPtr>& InDelegate )
示例
MyDYMulDelegate.AddDynamic(this, &ThisClass::DynamicMulPrint);
MyDYMulDelegate.Remove(this, FName("DynamicMulPrint"));
FScriptDelegate TempDyMulDelegate;
TempDyMulDelegate.BindUFunction(this, FName("DynamicMulPrint"));
MyDYMulDelegate.Add(TempDyMulDelegate);
MyDYMulDelegate.Remove(TempDyMulDelegate);
Remove
输入的FunctionName无效不会触发error
作用:根据UObject和函数指针解除绑定
MyDYMulDelegate.RemoveDynamic(this, &ThisClass::DynamicMulPrint);
作用:解除特定UObject的所有绑定
MyDYMulDelegate.RemoveAll(this);
作用:解除动态多播的所有绑定
MyDYMulDelegate.Clear();
作用:判断特定UObject的函数指针是否绑定
MyDYMulDelegate.AddDynamic(this, &ThisClass::DynamicMulPrint);
bool bIsBound = MyDYMulDelegate.IsAlreadyBound(this, &ThisClass::DynamicMulPrint);//true=1
UE_LOG(LogTemp, Warning, TEXT("%d"), bIsBound);
作用:根据FScriptDelegate对象或者FunctionName判断是否绑定
FScriptDelegate TempDyMulDelegate;
TempDyMulDelegate.BindUFunction(this, FName("DynamicMulPrint"));
MyDYMulDelegate.Add(TempDyMulDelegate);
bool bIsBound = MyDYMulDelegate.Contains(TempDyMulDelegate);
UE_LOG(LogTemp, Warning, TEXT("%d"), bIsBound);
MyDYMulDelegate.AddDynamic(this, &ThisClass::DynamicMulPrint);
bool bIsBound = MyDYMulDelegate.Contains(this, FName("DynamicMulPrint"));
UE_LOG(LogTemp, Warning, TEXT("%d"), bIsBound);
Events(事件)本质上是一个静态多播委托,但只有声明事件的类可以调用事件 的 Broadcast
、IsBound
和 Clear
函数。这意味着事件对象可在公共接口中公开,而无需让外部类访问这些敏感度函数。
事件使用情况有:
和静态多播一样
UPROPERTY()
Event的声明和静态多播委托声明方式几乎相同,唯一的区别是它们使用Event特有的宏变体
//不带参数
DECLARE_EVENT(ADelegateTest, FTestEvent);
//1个参数
DECLARE_EVENT_OneParam(ADelegateTest, FTestEvent, int32);
注意事项:
DECLARE_EVENT
宏的首个参数是"拥有"此Event的类,因此它可调用 Broadcast() 函数class FTestEvent : public TBaseMulticastDelegate<void, int32>
{
friend class ADelegateTest;
}
一个简单的Event定义如下:
UCLASS() class STUDY427TEST_API ADelegateTest : public AActor { GENERATED_BODY() public: DECLARE_EVENT_OneParam(ADelegateTest, FTestEvent, int32); FTestEvent& OnTestEvent() { return MyTestEvent; } private: FTestEvent MyTestEvent; }
基础类
/** Register/Unregister a callback for when assets are added to the registry */
DECLARE_EVENT_OneParam( IAssetRegistry, FAssetAddedEvent, const FAssetData&);
virtual FAseetAddedEvent& OnAssetAdded() = 0;
派生类
DECLARE_DERIVED_EVENT( FAssetRegistry, IAssetRegistry::FAssetAddedEvent, FAssetAddedEvent);
virtual FassetAddedEvent& OnAssetAdded() override { return AssetAddedEvent; }
注意事项: 在派生类中声明一个派生事件时,不要在 DECLARE_DERIVED_EVENT
宏中重复函数签名(参数const FAssetData&)。此外,DECLARE_DERIVED_EVENT
宏的最后一个参数是事件的新命名,通常与基础类型相同。
友元关系不会继承,允许派生类Broadcast
其事件的基础类需要将公开Broadcast
事件的封装函数设为protected
基础类:
public:
/** Broadcasts whenever the layer changes */
DECLARE_EVENT( FLayerViewModel, FChangedEvent )
FChangedEvent& OnChanged() { return ChangedEvent; }
protected:
void BroadcastChanged()
{
ChangedEvent.Broadcast();
}
private:
/** Broadcasts whenever the layer changes */
FChangedEvent ChangedEvent;
Event的绑定方法,解除绑定方法都和静态多播委托一样,这里就不举例子了。只需要注意一点,只有Event的友元类才可以直接进行 Broadcast
、IsBound
和 Clear
等函数
Delegate的使用方法总结终于告一段落,文章比较长,若有错误,欢迎指出。学疏才浅,还请见谅
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。