UE4 代理(Delegate)源码浅析(2)

本文章只是我个人在学习虚幻引擎过程中的理解,不一定正确,若有说的不对的地方,欢迎指正。

以下引用的源码在Engine\Source\Runtime\Core\Public\Delegates\文件夹下的文件内均可找到

在《UE4 代理(Delegate)源码浅析(1)》中我们浅析了一下静态单播代理的源码,这次我们继续讲讲剩下的代理。

一.静态多播代理

静态多播代理和静态单播代理的代码结构有许多相似的地方,所以不会讲的像单播那样详细。多播的名字上多了个**MULTICAST**字样,静态多播代理就是在内部保存一个单播代理的数组,添加绑定就是把绑定函数加入到数组中,执行的时候再依次调用所有绑定的函数,以此来实现多播的效果。

1.大致框架:

我们以DECLARE_MULTICAST_DELEGATE_OneParam为例,先来看看它的基本实现:

#define DECLARE_MULTICAST_DELEGATE_OneParam( DelegateName, Param1Type ) FUNC_DECLARE_MULTICAST_DELEGATE( DelegateName, void, Param1Type )

和单播代理一样最终封装为宏,传入两个宏参数——代理名(DelegateName),参数类型(Param1Type)。再传给FUNC_DECLARE_MULTICAST_DELEGATE宏,再来看看它的定义:

#define FUNC_DECLARE_MULTICAST_DELEGATE( MulticastDelegateName, ... ) \
	typedef TMulticastDelegate<__VA_ARGS__> MulticastDelegateName;

FUNC_DECLARE_MULTICAST_DELEGATE接受代理名(MulticastDelegateName)和可变参数。它用类型重定义关键字,把传入可变参数作为模板参数传给模板类TMulticastDelegate,类型重定义为MulticastDelegateName。到这里源码和单播都差不多,下面正式进入不一样的地方。
先看看TMulticastDelegate模板类的定义:

template <typename... ParamTypes>
class TMulticastDelegate<void, ParamTypes...> : public TBaseMulticastDelegate<void, ParamTypes... >
{
private:
	// Prevents erroneous use by other classes.
	typedef TBaseMulticastDelegate< void, ParamTypes... > Super;
};

TMulticastDelegate的定义挺简单只是重命名一下父类,再把传入的模板参数传给父类TBaseMulticastDelegate,再来看看TBaseMulticastDelegate的大致源码:

template <typename... ParamTypes>
class TBaseMulticastDelegate<void, ParamTypes...> : public FMulticastDelegateBase<FWeakObjectPtr>
{
	typedef FMulticastDelegateBase<FWeakObjectPtr> Super;

public:
	typedef TBaseDelegate< void, ParamTypes... > FDelegate;

	typedef IBaseDelegateInstance<void(ParamTypes...)> TDelegateInstanceInterface;

public:

	FDelegateHandle Add(const FDelegate& InNewDelegate)
	{}

	//……
	//一系列添加代理的函数(AddStatic、AddStatic、AddWeakLambda、AddRaw、AddSP、AddThreadSafeSP、AddUFunction、AddUObject) 
	//……
	
public:
	//移除代理绑定
	bool Remove(FDelegateHandle Handle)
	{}

protected:
	FDelegateHandle AddDelegateInstance(FDelegate&& InNewDelegate)
	{}

public:
	//执行代理
	void Broadcast(ParamTypes... Params) const
	{}

protected:
	bool RemoveDelegateInstance(FDelegateHandle Handle)
	{}
};

从源码可以看到TBaseMulticastDelegate定义了许多种添加绑定函数的方法,每种添加方法根据自己的情况做些处理,最终都会调用Add来,而Add会调用正在实现添加的AddDelegateInstance方法。
暴露了移除某个绑定函数的接口(Remove),注意只是移除一个指定的绑定函数,具体的移除实现再RemoveDelegateInstance中。另外还有执行代理(Broadcast)。具体的函数实现后面会讲。
现在来看看TBaseMulticastDelegate的父类,也是这条继承链的源头:

class FMulticastDelegateBase
{
public:
	//……

	void Clear()
	{}

	//……

	inline bool IsBound() const
	{}

	//……

	int32 RemoveAll(const void* InUserObject)
	{}

	//……

private:
	//……

	TInvocationList InvocationList;
};

FMulticastDelegateBase实现了我们常用的清除所有绑定函数的方法(Clear)、判断绑定的方法(IsBound)。注意这里的RemoveAllClear不同,RemoveAll是从调用列表中清除相关Object的绑定函数。
附上我自己做的关系图:

在这里插入图片描述

2.实现细节:

记得前文我们知道代理宏封装的是TMulticastDelegate,但多播代理的主要功能都是TMulticastDelegate的祖先(TBaseMulticastDelegateFMulticastDelegateBase)实现的。
现在我们就来看看它的一些方法和实现细节,首先是添加绑定函数,以AddUObject为例,源码:

template <typename UserClass, typename... VarTypes>
inline FDelegateHandle AddUObject(UserClass* InUserObject, typename TMemFunPtrType<true, UserClass, void (ParamTypes..., VarTypes...)>::Type InFunc, VarTypes... Vars)
{
	return Add(FDelegate::CreateUObject(InUserObject, InFunc, Vars...));
}

可以看出AddUObject是模板函数:
有两个模板参数——绑定函数所在类的类型(UserClass)和传给绑定函数的一系列参数的类型(VarTypes);
有三个函数参数——绑定函数所在类的实例(InUserObject)、绑定函数(InFunc)、传给绑定函数的一系列参数(Vars)。
AddUObject调用真正的添加方法Add。调用FDelegateCreateUObject,那么FDelegate是什么呢?来看看源码就知道了:

typedef TBaseDelegate<void, ParamTypes...> FDelegate;

看以看到,FDelegateTBaseDelegate的重定义。很熟悉吧,在上一章介绍单播的时候我们就介绍过TBaseDelegate和它的CreateUObject。这里就简单介绍一下——CreateUObject会根据传入的参数创建的并返回一个TBaseDelegate的实例。忘记的小伙伴可以回去《UE4 代理(Delegate)源码浅析(1)》看看它的实现细节。
我们把视线聚焦到Add上:

FDelegateHandle Add(const FDelegate& InNewDelegate)
{
	FDelegateHandle Result;
	if (Super::GetDelegateInstanceProtectedHelper(InNewDelegate))
	{
		Result = AddDelegateInstance(FDelegate(InNewDelegate));
	}

	return Result;
}

GetDelegateInstanceProtectedHelper是实现于FMulticastDelegateBase类内的方法,可以获取传入的待绑定代理的IDelegateInstance,判断一下它是否存在,若存在调用一下AddDelegateInstance传入代理类实例并添加。

注:IDelegateInstance我们在《UE4 代理(Delegate)源码浅析(1)》也是详细讲过的

关于AddDelegateInstance

FDelegateHandle AddDelegateInstance(FDelegate&& InNewDelegate)
{
	return Super::AddInternal(MoveTemp(InNewDelegate));
}

inline FDelegateHandle AddInternal(FDelegateBase&& NewDelegateBaseRef)
{
	//……
	FDelegateHandle Result = NewDelegateBaseRef.GetHandle();
	InvocationList.Add(MoveTemp(NewDelegateBaseRef));
	return Result;
}

AddDelegateInstance调用父类的AddInternal方法,传入代理实例。AddInternal使用代理函数表(InvocationList)的Add方法把代理实例添加到函数表中。
现在来看看执行代理(Broadcast)的核心源码:

void Broadcast(ParamTypes... Params) const
{
	//……

	Super::LockInvocationList();
	{
		//……

		for (int32 InvocationListIndex = LocalInvocationList.Num() - 1; InvocationListIndex >= 0; --InvocationListIndex)
		{
			const FDelegate& DelegateBase = (const FDelegate&)LocalInvocationList[InvocationListIndex];
			
			//获取代理函数的IDelegateInstance
			IDelegateInstance* DelegateInstanceInterface = Super::GetDelegateInstanceProtectedHelper(DelegateBase);
			if (DelegateInstanceInterface == nullptr || !((TDelegateInstanceInterface*)DelegateInstanceInterface)->ExecuteIfSafe(Params...))
			{
				//……
			}
		}
	}
	Super::UnlockInvocationList();
	
	//……
	
}

注:Super::LockInvocationList();和Super::UnlockInvocationList();是用来保证线程安全的锁

最主要的就是中间那个for循环了,它遍历了前文提到的代理函数表(InvocationList)。获取代理函数的IDelegateInstance,上一章讲过代理函数和代理类都保存在其中,最后调用它的ExecuteIfSafe执行保存的代理函数。
这样就实现了多播代理的广播效果,另外笔者看到源码加线程锁的操作,估计执行操作的不止一个线程,因此广播的执行顺序和添加代理函数的顺序不是一样的。

注:关于IDelegateInstance和它的继承链,在《UE4 代理(Delegate)源码浅析(1)》也详细讲过

至于解除代理绑定(Clear)就很简单了:

void Clear( )
{
	for (FDelegateBase& DelegateBaseRef : InvocationList)
	{
		DelegateBaseRef.Unbind();
	}

	CompactInvocationList(false);
}

前文说过多播就是保存了一个单播的数组,所以只要遍历一下调用函数表,获得每一个单播,然后调用一下Unbind就可以了。

二.事件

可能有小伙伴会问为什么要在这里讲事件呢?从代理的使用那一章我们知道,事件和多播差不多,但多了只有指定的类可以使用的限制。其实在实现上,事件和多播几乎一模一样,我们趁着刚说完多播的劲儿来说一下事件,很快就好。
DECLARE_EVENT_OneParam为例来看看它的源码:

#define DECLARE_EVENT_OneParam( OwningType, EventName, Param1Type ) FUNC_DECLARE_EVENT( OwningType, EventName, void, Param1Type )

#define FUNC_DECLARE_EVENT( OwningType, EventName, ... ) \
	class EventName : public TBaseMulticastDelegate<__VA_ARGS__> \
	{ \
		friend class OwningType; \
	};

基本实现还是熟悉的宏封装,在EventName中声明了一个友元(OwningType)。看起来事件的诸多功能不能被外部类使用,但其实不是这样的,只不过通过声明友元使指定的类拥有事件的全部访问权限。
事件的底层实现就是多播,事件声明了一个继承自TBaseMulticastDelegate的类EventName。熟悉吧,就是多播封装的类(TMulticastDelegate)的父类,忘记了可以回去上面看看。

三.本章小结:

这章紧接着上一章,浅析了一下多播代理的DECLARE_MULTICAST_DELEGATE_OneParam和事件的DECLARE_EVENT_OneParam,剩下的多播和事件基本差不多,想了解的话可以去源码里看,目录就在开头。本章就到这,谢谢观看 。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值