UE4之Delegate:組播

定義

#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的方法,跟單播裏那些方法都一樣,多播只不過是調用了單播的方法,生成一個單播的代理對象而已. 最後都會調用到父類 FMulticastDelegateBaseAddInternal方法.
還有最主要的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內部生成這個對象.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章