定義
#define FUNC_DECLARE_MULTICAST_DELEGATE( MulticastDelegateName, ... ) \
typedef TMulticastDelegate<__VA_ARGS__> MulticastDelegateName;
template <typename... ParamTypes>
class TMulticastDelegate<void, ParamTypes...> : public TBaseMulticastDelegate<void, ParamTypes... >
{
private:
typedef TBaseMulticastDelegate< void, ParamTypes... > Super;
};
多播的功能,基本上基於單播,因爲多播只是保存了多個單播而已.
多播實際上是一個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;
template <typename UserClass, typename... VarTypes>
inline FDelegateHandle AddRaw(UserClass* InUserObject, typename TMemFunPtrType<false, UserClass, void (ParamTypes..., VarTypes...)>::Type InFunc, VarTypes... Vars)
{
return Add(FDelegate::CreateRaw(InUserObject, InFunc, Vars...));
}
FDelegateHandle Add(FDelegate&& InNewDelegate)
{
FDelegateHandle Result;
if (Super::GetDelegateInstanceProtectedHelper(InNewDelegate))
{
Result = AddDelegateInstance(MoveTemp(InNewDelegate));
}
return Result;
}
FDelegateHandle Add(const FDelegate& InNewDelegate)
{
FDelegateHandle Result;
if (Super::GetDelegateInstanceProtectedHelper(InNewDelegate))
{
Result = AddDelegateInstance(FDelegate(InNewDelegate));
}
return Result;
}
FDelegateHandle AddDelegateInstance(FDelegate&& InNewDelegate)
{
return Super::AddInternal(MoveTemp(InNewDelegate));
}
void Broadcast(ParamTypes... Params) const
{
bool NeedsCompaction = false;
Super::LockInvocationList();
{
const TInvocationList& LocalInvocationList = Super::GetInvocationList();
// call bound functions in reverse order, so we ignore any instances that may be added by callees
for (int32 InvocationListIndex = LocalInvocationList.Num() - 1; InvocationListIndex >= 0; --InvocationListIndex)
{
// this down-cast is OK! allows for managing invocation list in the base class without requiring virtual functions
const FDelegate& DelegateBase = (const FDelegate&)LocalInvocationList[InvocationListIndex];
IDelegateInstance* DelegateInstanceInterface = Super::GetDelegateInstanceProtectedHelper(DelegateBase);
if (DelegateInstanceInterface == nullptr || !((TDelegateInstanceInterface*)DelegateInstanceInterface)->ExecuteIfSafe(Params...))
{
NeedsCompaction = true;
}
}
}
Super::UnlockInvocationList();
if (NeedsCompaction)
{
const_cast<TBaseMulticastDelegate*>(this)->CompactInvocationList();
}
}
};
可以看到的是, 類似於AddRaw的方法,跟單播裏那些方法都一樣,多播只不過是調用了單播的方法,生成一個單播的代理對象而已. 最後都會調用到父類 FMulticastDelegateBase的AddInternal方法.
還有最主要的Broadcast方法, 遍歷代理數組,如果執行不成功(ExecuteIfSafe失敗), 則標記這個代理數組需要壓縮(CompactInvocationList).
再看看FMulticastDelegateBase
template<typename ObjectPtrType>
class FMulticastDelegateBase
{
public:
~FMulticastDelegateBase(){}
void Clear( )
{
for (FDelegateBase& DelegateBaseRef : InvocationList)
{
DelegateBaseRef.Unbind();
}
CompactInvocationList(false);
}
// 檢查是否有任何功能綁定到此多播委託.
inline bool IsBound( ) const
{
for (const FDelegateBase& DelegateBaseRef : InvocationList)
{
if (DelegateBaseRef.GetDelegateInstanceProtected())
{
return true;
}
}
return false;
}
inline bool IsBoundToObject( void const* InUserObject ) const
{
for (const FDelegateBase& DelegateBaseRef : InvocationList)
{
IDelegateInstance* DelegateInstance = DelegateBaseRef.GetDelegateInstanceProtected();
if ((DelegateInstance != nullptr) && DelegateInstance->HasSameObject(InUserObject))
{
return true;
}
}
return false;
}
void RemoveAll( const void* InUserObject )
{
if (InvocationListLockCount > 0)
{
bool NeedsCompacted = false;
for (FDelegateBase& DelegateBaseRef : InvocationList)
{
IDelegateInstance* DelegateInstance = DelegateBaseRef.GetDelegateInstanceProtected();
if ((DelegateInstance != nullptr) && DelegateInstance->HasSameObject(InUserObject))
{
// Manually unbind the delegate here so the compaction will find and remove it.
DelegateBaseRef.Unbind();
NeedsCompacted = true;
}
}
// can't compact at the moment, but set out threshold to zero so the next add will do it
if (NeedsCompacted)
{
CompactionThreshold = 0;
}
}
else
{
// compact us while shuffling in later delegates to fill holes
for (int32 InvocationListIndex = 0; InvocationListIndex < InvocationList.Num();)
{
FDelegateBase& DelegateBaseRef = InvocationList[InvocationListIndex];
IDelegateInstance* DelegateInstance = DelegateBaseRef.GetDelegateInstanceProtected();
if (DelegateInstance == nullptr
|| DelegateInstance->HasSameObject(InUserObject)
|| DelegateInstance->IsCompactable())
{
InvocationList.RemoveAtSwap(InvocationListIndex, 1, false);
}
else
{
InvocationListIndex++;
}
}
CompactionThreshold = FMath::Max(2, 2 * InvocationList.Num());
InvocationList.Shrink();
}
}
protected:
inline FMulticastDelegateBase( )
: CompactionThreshold(2)
, InvocationListLockCount(0) { }
protected:
inline FDelegateHandle AddInternal(FDelegateBase&& NewDelegateBaseRef)
{
// compact but obey threshold of when this will trigger
CompactInvocationList(true);
FDelegateHandle Result = NewDelegateBaseRef.GetHandle();
InvocationList.Add(MoveTemp(NewDelegateBaseRef));
return Result;
}
void CompactInvocationList(bool CheckThreshold=false)
{
if (InvocationListLockCount > 0)
{
return;
}
//如果檢查閾值,則服從但衰減. 這是爲了確保即使很少調用的委託也將最終在Add()中壓縮
if (CheckThreshold && --CompactionThreshold > InvocationList.Num())
{
return;
}
int32 OldNumItems = InvocationList.Num();
// 查找任何null或可壓縮的內容並將其刪除
for (int32 InvocationListIndex = 0; InvocationListIndex < InvocationList.Num();)
{
FDelegateBase& DelegateBaseRef = InvocationList[InvocationListIndex];
IDelegateInstance* DelegateInstance = DelegateBaseRef.GetDelegateInstanceProtected();
if (DelegateInstance == nullptr || DelegateInstance->IsCompactable())
{
InvocationList.RemoveAtSwap(InvocationListIndex);
}
else
{
InvocationListIndex++;
}
}
CompactionThreshold = FMath::Max(2, 2 * InvocationList.Num());
if (OldNumItems > CompactionThreshold)
{
// would be nice to shrink down to threshold, but reserve only grows..?
InvocationList.Shrink();
}
}
inline const TInvocationList& GetInvocationList( ) const
{
return InvocationList;
}
inline void LockInvocationList( ) const
{
++InvocationListLockCount;
}
inline void UnlockInvocationList( ) const
{
--InvocationListLockCount;
}
protected:
static FORCEINLINE IDelegateInstance* GetDelegateInstanceProtectedHelper(const FDelegateBase& Base)
{
return Base.GetDelegateInstanceProtected();
}
private:
/** Holds the collection of delegate instances to invoke. */
TInvocationList InvocationList;
/** Used to determine when a compaction should happen. */
int32 CompactionThreshold;
/** Holds a lock counter for the invocation list. */
mutable int32 InvocationListLockCount;
};
RemoveAll裏,因爲存在多線程訪問的問題,因此會先根據InvocationListLockCount > 0進行判斷是否有別的線程正在訪問,否則就移除和參數InUserObject相關的代理.
FMulticastDelegateBase裏,有個很重要的函數AddInternal, 就是上文提及到的, 所有TBaseMulticastDelegate的Add方法,最後都會調用到AddInternal
inline FDelegateHandle AddInternal(FDelegateBase&& NewDelegateBaseRef)
{
CompactInvocationList(true);
FDelegateHandle Result = NewDelegateBaseRef.GetHandle();
InvocationList.Add(MoveTemp(NewDelegateBaseRef));
return Result;
}
首先會調用CompactInvocationList壓縮一次代理數組, 這個方法裏最主要的判斷就是判斷代理類的IsCompacktable()
if (DelegateInstance == nullptr || DelegateInstance->IsCompactable())
{
InvocationList.RemoveAtSwap(InvocationListIndex);
}
查看所有的代理實現類裏,目前只有TBaseUFunctionDelegateInstance, TBaseUObjectMethodDelegateInstance和TWeakBaseFunctorDelegateInstance重寫了基類IsCompactable方法,只看看基類的實現
virtual bool IsCompactable( ) const
{
return !IsSafeToExecute();
}
可以看到,內部是調用了IsSafeToExecute,IsSafeToExecute在各個子類裏,也有不同,但基本都是判斷代理類是否合理(Valid).
內部有成員變量TInvocationList InvocationList來保存所有代理類FDelegateBase
typedef TArray<FDelegateBase, FMulticastInvocationListAllocatorType> TInvocationList;
FDelegateBase代理類, 內部保存了具體代理對象的實現,單播裏已經說過, 比如我們在Add各種操作的時候,就會在FDelegateBase內部生成這個對象.