日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UE4反射原理(转)

發布時間:2024/1/1 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UE4反射原理(转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自:
https://blog.csdn.net/mohuak/article/details/81913532
https://blog.csdn.net/u012999985/article/details/52902065

1. UE4反射系統

什么是反射系統
在UE4里面,你無時無刻都會看到類似UFUNCTION()這樣的宏。官方文檔告訴你,只要在一個函數的前面加上這個宏,然后在括號里面加上BlueprintCallable就可以在編輯器里面調用了。按照他的指示,我們就能讓我們的函數實現各種各樣特別的功能,那這個效果就是通過UE4的反射系統來實現的。
其實,所謂反射,是程序在運行時進行自檢的一種能力,自檢什么呢?我認為就是檢查自己的C++類,函數,成員變量,結構體等等(對應起來也就是大家在UE4能看到的UCLASS,UFUNCTON,UPROPERTY,USTRUCT后面還會提到)。
那檢查這些東西做什么呢?最明顯的就是支持藍圖和C++的交互功能,說的更通俗一點,就是可以更自由的控制這些結構,讓他在我們想出現的地方出現,讓他在我們想使用的地方使用。要知道我們在虛幻4中聲明的任意一個類,都是繼承于UObject類的,所以他遠遠不是我們所以為的那個普通的C++類。我們可以使用這個類進行網絡復制,執行垃圾回收,讓他和藍圖交互等等。而這一切原生的C++是并不支持的,也正是因此虛幻4才構建了一個這樣的反射系統。

2. 反射實現機制和基本原理

在了解反射系統前,我們必須要知道兩個UE4獨特的文件類型—“.generate.h”以及“.generate.cpp”。“.generate.h”文件在每一個類的聲明前面都會被包含到對應的頭文件里面。(這也是官方建議我們要用編輯器來創建類的原因,他們并不是常規的C++類)而“.generate.cpp”對于一整個項目只會有一個。這兩種文件可以說是反射系統的關鍵所在,他們是通過Unreal Build Tool(UBT) 和UnrealHeaderTool(UHT)來生成的。

2.1 UBT 和UHT
UnrealBuildTool(UBT,C#):UE4的自定義工具,來編譯UE4的逐個模塊并處理依賴等。我們編寫的Target.cs,Build.cs都是為這個工具服務的。
UnrealHeaderTool (UHT,C++):UE4的C++代碼解析生成工具,我們在代碼里寫的那些宏UCLASS等和#include “*.generated.h”都為UHT提供了信息來生成相應的C++反射代碼。
代碼編譯在兩個階段中進行:1.UHT 被調用。它將解析 C++ 頭中引擎相關類元數據,并生成自定義代碼,以實現諸多 UObject 相關的功能。2.普通 C++ 編譯器被調用,以便對結果進行編譯。)
思考:UHT如何實現反射?

打個比方:UClass其實就好比一張表,一張戶口本的東西,指向”真實家庭“的指針。上面記錄著一些信息,好比:

張三:

1995年出生
李四:

1991年出生

那虛幻引擎是如何實現這個機制的呢?一種方法是,一開始編譯的時候,把表格都填好,放到一個文件處,要找某家人的時候再取出來,但是有一個問題就是,每編譯一次的時候,函數地址會發生變化,所以直接存儲函數地址這種方法不行。第二種方法就是,存儲”進行查找戶口調查的過程”,比方說,它存儲了每個家庭哪些人需要登記信息。然后當運行開始的時候,逐個讓每一家人進行登記。而UHT就是在為這個過程提供幫助,它生成的.generate.h 和 .generate.cpp就是存儲進行查找戶口調查的過程。

2.2 .generate.h 和 .generate.cpp
“.generate.h”與“.generate.cpp”文件里面都是什么?有什么作用?

“.generate.h”里面是宏,而且包含一個非常龐大的宏,這個宏把所有和反射相關的方法(包括定義)和結構體連接到一起。

而“.generate.cpp”里面是許多的函數定義,UHT根據你在頭文件里面使用的宏(UFUNCTION等)自動的生成這個文件,所以這個文件并不需要你去修改,也不允許修改。UBT屬性通過掃描頭文件,記錄任何至少有一個反射類型的頭文件的模塊。如果其中任意一個頭文件從上一次編譯起發生了變化,那么 UHT就會被調用來利用和更新反射數據。UHT分析頭文件,創建一系列反射數據,并且生成包含反射數據的C++代碼。

2.3 反射類型和層次結構
官方文檔所給出的基本層次結構

UFieldUStructUClass (C++ class)UScriptStruct (C++ struct)UFunction (C++ function)UEnum (C++ enumeration)UProperty (C++ member variable or function parameter)(Many subclasses for different types)

下圖引自InsideUE4

2.4 熱重載
? 在編輯器模式下,UE4將工程代碼編譯成動態鏈接庫,這樣編輯器可以動態的加載和卸載某個動態鏈接庫。UE4為工程自動生成一個cpp文件(本工程為HelloUE4.generated.cpp),cpp文件包含了當前工程中所有需要反射的類信息,以及類成員列表和每個成員的類型信息。在動態鏈接庫被編輯器加載的時候,自動將類信息注冊到編輯器中。反之,卸載的時候,這樣類信息也會被反注冊。
? 在開發的過程中,當我們編譯完成工程的時候,UE4編輯器會自動檢測動態鏈接庫的變化,然后自動熱重載這些動態鏈接庫中的類信息。

3. 反射代碼實例分析

UE4引擎啟動時候,是分Module(dll)來構建類型信息的。Module采用模擬一般程序的構建流程的方法,大致需要以下幾個階段:生成、收集、注冊、鏈接。

生成階段:借助UHT(Unreal Header Tool)工具,生成UClass代碼,包括UClass構造,注冊屬性和函數等;
收集階段:利用Static自動注冊方式,在模塊加載的時候,將所有UClass登記,集中在Array管理
注冊階段:在模塊初始化的時候,將Array中的所有UClass相關的Function和Property注冊;
鏈接階段:就是反射功能。

生成階段

要讓一個類支持反射,你需要讓這個類要繼承自UObject、在類聲明前添加UCLASS(或USTRUCT)標識,并且include “xxx.generated.h”頭文件(而且必須是最后一個include)。

當你啟動UE4編譯時,UE4會首先運行UHT (UnrealHeaderTool),UHT成功運行后才會執行真正的編譯。UHT是一個頭文件解析和代碼生成工具,它會處理所有的頭文件,從中檢索UCLASS、GENERATED_BODY、UPROPERTY、UFUNTION等關鍵字,檢索到以后就為它們生成.generate.h 和 .generate.cpp。

以下是創建的一個小小的demo來對反射進行更好的理解

創建一個新的項目,然后創建一個類繼承AGamoModeBase。

// Fill out your copyright notice in the Description page of Project Settings. #pragma once#include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" #include "HelloGameMode.generated.h" // 核心內容,必須放在最后一行,由UBT自動生成。UCLASS() class HELLOWORLD_API AHelloGameMode : public AGameModeBase {GENERATED_BODY() // 重中之重 protected:UPROPERTY(BlueprintReadWrite, Category = "AReflectionStudyGameMode")float Score;UFUNCTION(BlueprintCallable, Category = "AReflectionStudyGameMode")void CallableFuncTest();UFUNCTION(BlueprintNativeEvent, Category = "AReflectionStudyGameMode")void NavtiveFuncTest();UFUNCTION(BlueprintImplementableEvent, Category = "AReflectionStudyGameMode")void ImplementableFuncTest();};

首先UHT幫我自動生成了四個文件。

HelloGameMode.generated.h
UHT分析生成的文件內容如下:由于篇幅原因,只列出一部分代碼。

// ...// ... #define helloworld_Source_helloworld_Public_HelloGameMode_h_15_RPC_WRAPPERS_NO_PURE_DECLS \virtual void NavtiveFuncTest_Implementation(); \\DECLARE_FUNCTION(execNavtiveFuncTest) \{ \P_FINISH; \P_NATIVE_BEGIN; \P_THIS->NavtiveFuncTest_Implementation(); \P_NATIVE_END; \} \\DECLARE_FUNCTION(execCallableFuncTest) \{ \P_FINISH; \P_NATIVE_BEGIN; \P_THIS->CallableFuncTest(); \P_NATIVE_END; \}#define helloworld_Source_helloworld_Public_HelloGameMode_h_15_EVENT_PARMS #define helloworld_Source_helloworld_Public_HelloGameMode_h_15_CALLBACK_WRAPPERS #define helloworld_Source_helloworld_Public_HelloGameMode_h_15_INCLASS_NO_PURE_DECLS \ private: \static void StaticRegisterNativesAHelloGameMode(); \friend struct Z_Construct_UClass_AHelloGameMode_Statics; \ public: \DECLARE_CLASS(AHelloGameMode, AGameModeBase, COMPILED_IN_FLAGS(0 | CLASS_Transient), CASTCLASS_None, TEXT("/Script/helloworld"), NO_API) \DECLARE_SERIALIZER(AHelloGameMode)#define helloworld_Source_helloworld_Public_HelloGameMode_h_15_ENHANCED_CONSTRUCTORS \/** Standard constructor, called after all reflected properties have been initialized */ \NO_API AHelloGameMode(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \ private: \/** Private move- and copy-constructors, should never be used */ \NO_API AHelloGameMode(AHelloGameMode&&); \NO_API AHelloGameMode(const AHelloGameMode&); \ public: \DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AHelloGameMode); \ DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AHelloGameMode); \DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AHelloGameMode)#define helloworld_Source_helloworld_Public_HelloGameMode_h_15_PRIVATE_PROPERTY_OFFSET \FORCEINLINE static uint32 __PPO__Score() { return STRUCT_OFFSET(AHelloGameMode, Score); }#define helloworld_Source_helloworld_Public_HelloGameMode_h_15_GENERATED_BODY \ PRAGMA_DISABLE_DEPRECATION_WARNINGS \ public: \helloworld_Source_helloworld_Public_HelloGameMode_h_15_PRIVATE_PROPERTY_OFFSET \ // 關于UPROPERTY部分,具體還沒研究透,看代碼像是獲取指針地址的偏移值helloworld_Source_helloworld_Public_HelloGameMode_h_15_RPC_WRAPPERS_NO_PURE_DECLS \ //helloworld_Source_helloworld_Public_HelloGameMode_h_15_CALLBACK_WRAPPERS \ // 空宏helloworld_Source_helloworld_Public_HelloGameMode_h_15_INCLASS_NO_PURE_DECLS \helloworld_Source_helloworld_Public_HelloGameMode_h_15_ENHANCED_CONSTRUCTORS \ private: \ PRAGMA_ENABLE_DEPRECATION_WARNINGS#undef CURRENT_FILE_ID #define CURRENT_FILE_ID helloworld_Source_helloworld_Public_HelloGameMode_hPRAGMA_ENABLE_DEPRECATION_WARNINGS

GENERATED_BODY

我們在HelloGameMode.cpp中發現,有一個GENERATED_BODY() 宏,該宏是重中之重,其他的UCLASS宏只是提供信息,不參與編譯,而GENERATED_BODY正是把聲明和元數據定義關聯到一起的樞紐。繼續查看宏定義。 GENERATED_BODY()的宏定義 :

#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D #define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D) #define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY)

而在。generated.h Line63中定義了CURRENT_FILE_ID。所以GENERATED_BODY() 展開就是helloworld_Source_helloworld_Public_HelloGameMode_h_15_GENERATED_BODY,而這個宏的定義則在generated.h Line51。

DECLARE_FUNCTION
通過helloworld_Source_helloworld_Public_HelloGameMode_h_15_GENERATED_BODY向上, 以helloworld_Source_helloworld_Public_HelloGameMode_h_15_RPC_WRAPPERS_NO_PURE_DECLS為例:

#define helloworld_Source_helloworld_Public_HelloGameMode_h_15_INCLASS_NO_PURE_DECLS \ private: \static void StaticRegisterNativesAHelloGameMode(); \ // 在HelloGameMode.gen.cpp中實現friend struct Z_Construct_UClass_AHelloGameMode_Statics; \ // 在HelloGameMode.gen.cpp中定義 public: \DECLARE_CLASS(AHelloGameMode, AGameModeBase, COMPILED_IN_FLAGS(0 | CLASS_Transient), CASTCLASS_None, TEXT("/Script/helloworld"), NO_API) \DECLARE_SERIALIZER(AHelloGameMode) // 序列化,先忽略

DECLARE_CLASS是最重要的一個聲明,對照著定義:DECLARE_CLASS(AHelloGameMode, AGameModeBase, COMPILED_IN_FLAGS(0 | CLASS_Transient), CASTCLASS_None, TEXT("/Script/helloworld"), NO_API)
TClass:類名
TSuperClass:基類名字
TStaticFlags:類的屬性標記
TStaticCastFlags:指定了該類可以轉換為哪些類,這里為0表示不能轉為那些默認的類,讀者可以自己查看EClassCastFlags聲明來查看具體有哪些默認類轉換。
TPackage:類所處于的包名,所有的對象都必須處于一個包中,而每個包都具有一個名字,可以通過該名字來查找。這里是”/Script/helloworld”,指定是Script下的helloworld,Script可以理解為用戶自己的實現,不管是C++還是藍圖,都可以看作是引擎外的一種腳本,當然用這個名字也肯定有UE3時代UnrealScript的影子。Hello就是項目名字,該項目下定義的對象處于該包中。Package的概念涉及到后續Object的組織方式,目前可以簡單理解為一個大的Object包含了其他子Object。
TRequiredAPI:就是用來Dll導入導出的標記,這里是NO_API1,因為是最終exe,不需要導出。

DefaultConstructor默認構造
helloworld_Source_helloworld_Public_HelloGameMode_h_15_ENHANCED_CONSTRUCTORS 部分:

#define helloworld_Source_helloworld_Public_HelloGameMode_h_15_ENHANCED_CONSTRUCTORS \/** Standard constructor, called after all reflected properties have been initialized */ \NO_API AHelloGameMode(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; \ //根據注釋的意思,就是標準的構造函數,在所有反射的屬性初始化后調用。 private: \/** Private move- and copy-constructors, should never be used */ \ //永遠不要用這兩個函數NO_API AHelloGameMode(AHelloGameMode&&); \ //移動構造函數NO_API AHelloGameMode(const AHelloGameMode&); \ //拷貝構造函數 public: \DECLARE_VTABLE_PTR_HELPER_CTOR(NO_API, AHelloGameMode); \ // 熱加載,先忽略 DEFINE_VTABLE_PTR_HELPER_CTOR_CALLER(AHelloGameMode); \ // 空宏DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AHelloGameMode) // 定義一個構造函數

繼續查看DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL的定義:

#define DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(TClass) \static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }聲明定義了一個構造函數包裝器。需要這么做的原因是,在根據名字反射創建對象的時候,需要調用該類的構造函數。可是類的構造函數并不能用函數指針指向,因此這里就用一個static函數包裝一下,變成一個”平凡”的函數指針而且所有類的簽名一致,就可以在UClass里用一個函數指針里保存起來 。

HelloGameMode.gen.cpp
由于這個類代碼太長,所以的話就分版塊討論。

ProcessEvent

static FName NAME_AHelloGameMode_ImplementableFuncTest = FName(TEXT("ImplementableFuncTest")); void AHelloGameMode::ImplementableFuncTest() {ProcessEvent(FindFunctionChecked(NAME_AHelloGameMode_ImplementableFuncTest),NULL); } 為啥BlueprintImplementableEvent的函數不用我們去實現呢,是因為UBT幫我們自己實現了。 而關于ProcessEvent部分, 這個方法在UObject中實現的。

剛接觸UE4的時候,如果是BlueprintImplementabeEvent的函數,是不是發現不需要自己去實現,那么當時有沒有覺得怪異呢,上面的代碼就解釋清楚了,那是UE4幫我們實現了,可以看到它調用了ProcessEvent方法,這個方法在UObject中實現的。
而且如果是BlueprintImplementabeEvent或者RPC的那些函數,我們是不需要實現其函數方法的,如果在CPP文件定義實現的話,則會報錯,那是因為在.gen.cpp中已經幫我們實現了這個函數。

收集階段

自動化注冊
思考:UE4如何實現自動注冊的呢?
主要是通過Static 自動注冊的方式。
而在本例子是如何通過Static自動注冊呢?
回顧生成階段的IMPLEMENT_CLASS和ConstructClass中靜態聲明了兩個變量。

#define IMPLEMENT_CLASS(TClass, TClassCrc) \static TClassCompiledInDefer<TClass> AutoInitialize##TClass(TEXT(#TClass), sizeof(TClass), TClassCrc); \

或者

static FCompiledInDefer Z_CompiledInDefer_UClass_AHelloGameMode(Z_Construct_UClass_AHelloGameMode, &AHelloGameMode::StaticClass, TEXT("/Script/helloworld"), TEXT("AHelloGameMode"), false, nullptr, nullptr, nullptr);

這兩個全局靜態變量在Main函數之前就會初始化

初始化就會調用這兩個變量的構造函數

構造函數會分別調用UClassCompiledInDefer函數和UObjectCompiledInDefer函數。

這兩個函數會把ClassInfo放進延遲注冊的數組中去。

思考:為何需要TClassCompiledInDefer和FCompiledInDefer兩個靜態初始化來登記?

我們也觀察到了這二者是一一對應的,問題是為何需要兩個靜態對象來分別收集,為何不合二為一?關鍵在于我們首先要明白它們二者的不同之處,前者的目的主要是為后續提供一個TClass::StaticClass的Register方法(其會觸發GetPrivateStaticClassBody的調用,進而創建出UClass對象),而后者的目的是在其UClass身上繼續調用構造函數,初始化屬性和函數等一些注冊操作。我們可以簡單理解為就像是C++中new對象的兩個步驟,首先分配內存,繼而在該內存上構造對象。我們在后續的注冊章節里還會繼續討論到這個問題。

思考:為啥要采用延遲注冊的方式?為什么不在收集階段直接注冊呢?

主要考慮到UE4超多類,都在static初始化階段注冊,程序會表現啟動速度慢,用戶雙擊程序,沒反應。所以采用延遲注冊。

啟動過程分析

上面我們講解了這個注冊信息的過程,而它們的執行是伴隨著當前模塊的加載而執行的,我們都知道靜態變量的初始化是先于Main函數執行的。下面我們簡單畫了一下虛幻編輯器的啟動流程,這樣我們就可以準確地看到整個注冊反射信息的過程了。

void ProcessNewlyLoadedUObjects() {// ...UClassRegisterAllCompiledInClasses();const TArray<UClass* (*)()>& DeferredCompiledInRegistration = GetDeferredCompiledInRegistration();const TArray<FPendingStructRegistrant>& DeferredCompiledInStructRegistration = GetDeferredCompiledInStructRegistration();const TArray<FPendingEnumRegistrant>& DeferredCompiledInEnumRegistration = GetDeferredCompiledInEnumRegistration();bool bNewUObjects = false;while( GFirstPendingRegistrant || DeferredCompiledInRegistration.Num() || DeferredCompiledInStructRegistration.Num() || DeferredCompiledInEnumRegistration.Num() ){bNewUObjects = true;UObjectProcessRegistrants();UObjectLoadAllCompiledInStructs();UObjectLoadAllCompiledInDefaultProperties();} // ... }

在這個函數中,可以看到void ProcessNewlyLoadedUObjects()這個函數就是我們主要關注的函數,我們前面講到的注冊的信息,包括類、結構體以及枚舉類型的反射信息都會在這里進行注冊。

收集
在生成階段中,我們生成了很多Class、Property、Function的信息。但是我們需要它們的信息收集整合到我們需要的數據結構里保存,以便下一階段的使用。

通過UClassCompiledInDefer收集

深入UClassCompiledInDefer方法我們可以發現。

void UClassCompiledInDefer(FFieldCompiledInInfo* ClassInfo, const TCHAR* Name, SIZE_T ClassSize, uint32 Crc) {// We will either create a new class or update the static class pointer of the existing oneGetDeferredClassRegistration().Add(ClassInfo); //static TArray<FFieldCompiledInInfo*> DeferredClassRegistration; }

可以看到UClassCompiledInDefer(this, InName, InClassSize, InCrc),傳進去4個參數,主要是收集三個數據,為啥要傳4個參數呢,是因為把自己的指針傳進去,為了方便后續調用Register()方法。

實現的功能主要是在一個靜態Array里添加信息記錄。

這個數組會在后續注冊Class的時候會被調用,然后調用Register(),實現注冊功能。

/** Register all loaded classes */ void UClassRegisterAllCompiledInClasses() {//...TArray<FFieldCompiledInInfo*>& DeferredClassRegistration = GetDeferredClassRegistration();for (const FFieldCompiledInInfo* Class : DeferredClassRegistration){UClass* RegisteredClass = Class->Register(); //實現注冊,關鍵步驟。 #if WITH_HOT_RELOADif (GIsHotReload && Class->OldClass == nullptr){AddedClasses.Add(RegisteredClass);} #endif}DeferredClassRegistration.Empty(); //注冊完成,清空注冊表//...

而Register函數實際上傳進來的就是StaticClass函數

接著就是StaticClass()部分如何收集的解釋。

通過StaticClass()我們可以發現主要是調用了GetPrivateStaticClass(),而GetPrivateStaticClass()也主要是調用了GetPrivateStaticClassBody()函數,那么通過這個函數收集到了哪些信息呢。

以下是主要的信息:

**PackageName:**StaticPackage()

Name:類名,+1去掉U、A、F前綴,+11去掉_Deprecated前綴

ReturnClass:輸出引用,也就是收集信息后產生的UClass,PrivateStaticClass

void(*RegisterNativeFunc)(): StaticRegisterNativesAHelloGameMode(), 收集原生的函數。

InSize: 類的大小

InClassConstructor:構造函數,在DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AHelloGameMode)實現。

InSuperClassFn:&TClass::Super::StaticClass, 父類的注冊函數。

// in DECLARE_CLASS inline static UClass* StaticClass() {return GetPrivateStaticClass(); }// in IMPLEMENT_CLASS UClass* TClass::GetPrivateStaticClass() {static UClass* PrivateStaticClass = NULL;if (!PrivateStaticClass){/* this could be handled with templates, but we want it external to avoid code bloat */// 主要實現內容GetPrivateStaticClassBody(StaticPackage(),(TCHAR*)TEXT(#TClass) + 1 + ((StaticClassFlags & CLASS_Deprecated) ? 11 : 0), PrivateStaticClass, StaticRegisterNatives##TClass, // StaticRegisterNativesAHelloGameMode,sizeof(TClass), (EClassFlags)TClass::StaticClassFlags, TClass::StaticClassCastFlags(), TClass::StaticConfigName(), (UClass::ClassConstructorType)InternalConstructor<TClass>, //構造函數,在DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(AHelloGameMode) 實現(UClass::ClassVTableHelperCtorCallerType)InternalVTableHelperCtorCaller<TClass>, &TClass::AddReferencedObjects, &TClass::Super::StaticClass, &TClass::WithinClass::StaticClass ); } return PrivateStaticClass; }

這里主要是通過收集到的信息構造UClass。

主要有三部分:

首先給ReturnClass分配內存,然后初始化構造。
首先確保自己的父類已經注冊了,設置SuperStruct和 在全局PendingRegistrantsMaps中添加自己身類,延遲注冊。(在注冊階段會對這部分進行解釋)
Register the class’s native functions,注冊自身原生的函數、藍圖可執行的函數,執行 StaticRegisterNativesAHelloGameMode();

// 刪減部分代碼 void GetPrivateStaticClassBody(const TCHAR* PackageName,const TCHAR* Name,UClass*& ReturnClass,void(*RegisterNativeFunc)(),uint32 InSize,EClassFlags InClassFlags,EClassCastFlags InClassCastFlags,const TCHAR* InConfigName,UClass::ClassConstructorType InClassConstructor,UClass::ClassVTableHelperCtorCallerType InClassVTableHelperCtorCaller,UClass::ClassAddReferencedObjectsType InClassAddReferencedObjects,UClass::StaticClassFunctionType InSuperClassFn,UClass::StaticClassFunctionType InWithinClassFn,bool bIsDynamic /*= false*/) {if (!bIsDynamic){ReturnClass = (UClass*)GUObjectAllocator.AllocateUObject(sizeof(UClass), alignof(UClass), true); // 內存分配// 構造函數ReturnClass = ::new (ReturnClass)UClass(EC_StaticConstructor,Name,InSize,InClassFlags,InClassCastFlags,InConfigName,EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet),InClassConstructor,InClassVTableHelperCtorCaller,InClassAddReferencedObjects);check(ReturnClass);}// 設置SuperStruct和 在全局PendingRegistrantsMaps中添加自己身類,后續注冊UClass的時候會從Maps去除自身。InitializePrivateStaticClass(InSuperClassFn(),ReturnClass,InWithinClassFn(),PackageName,Name);// Register the class's native functions.// 注冊自身原生的函數。藍圖可執行的函數// 執行 StaticRegisterNativesAHelloGameMode();RegisterNativeFunc(); }

通過UObjectCompiledInDefer收集

深入UObjectCompiledInDefer方法我們可以發現:

void UObjectCompiledInDefer(UClass *(*InRegister)(), UClass *(*InStaticClass)(), const TCHAR* Name, const TCHAR* PackageName, bool bDynamic, const TCHAR* DynamicPathName, void (*InInitSearchableValues)(TMap<FName, FName>&)) {// ...TArray<UClass *(*)()>& DeferredCompiledInRegistration = GetDeferredCompiledInRegistration();checkSlow(!DeferredCompiledInRegistration.Contains(InRegister));DeferredCompiledInRegistration.Add(InRegister);// ... }

傳進去一個InRegister函數指針到延遲注冊數組中,也就是Z_Construct_UClass_AHelloGameMode函數。

然后在啟動階段會調用UObjectLoadAllCompiledInDefaultProperties函數,遍歷所有的延遲注冊數組,進行收集注冊

static void UObjectLoadAllCompiledInDefaultProperties() {static FName LongEnginePackageName(TEXT("/Script/Engine"));TArray<UClass *(*)()>& DeferredCompiledInRegistration = GetDeferredCompiledInRegistration();const bool bHaveRegistrants = DeferredCompiledInRegistration.Num() != 0;if( bHaveRegistrants ){TArray<UClass*> NewClasses;TArray<UClass*> NewClassesInCoreUObject;TArray<UClass*> NewClassesInEngine;TArray<UClass* (*)()> PendingRegistrants = MoveTemp(DeferredCompiledInRegistration);for (UClass* (*Registrant)() : PendingRegistrants){UClass* Class = Registrant(); // 在這一步調用了Z_Construct_UClass_AHelloGameMode函數//...}// ...} }

而Z_Construct_UClass_AHelloGameMode函數主要是調用了ConstructClass函數。
可以看到,Construct函數傳進去兩個參數,一個OuterClass,一個ClassParams。

OuterClass是我們注冊后得到的輸出對象。

而ClassParams是我們需要收集的數據。
以下讓我們對ClassParams的數據進行一些分析,下面是一些重要的數據部分:

ClassNoRegisterFunc: StaticClass,作用在上面有具體講述

DependencySingletonFuncArray: 所依賴的構建函數,確保自己所依賴部分都被構建成功。

FunctionLinkArray:需要反射的函數數組

PropertyArray: 屬性數組

const UE4CodeGen_Private::FClassParams Z_Construct_UClass_AHelloGameMode_Statics::ClassParams = {&AHelloGameMode::StaticClass,DependentSingletons, ARRAY_COUNT(DependentSingletons),0x009002A8u,FuncInfo, ARRAY_COUNT(FuncInfo),Z_Construct_UClass_AHelloGameMode_Statics::PropPointers, ARRAY_COUNT(Z_Construct_UClass_AHelloGameMode_Statics::PropPointers),nullptr,&StaticCppClassTypeInfo,nullptr, 0,METADATA_PARAMS(Z_Construct_UClass_AHelloGameMode_Statics::Class_MetaDataParams, ARRAY_COUNT(Z_Construct_UClass_AHelloGameMode_Statics::Class_MetaDataParams)) };struct FClassParams {UClass* (*ClassNoRegisterFunc)(); // 傳進來StaticClass的函數UObject* (*const *DependencySingletonFuncArray)(); // 傳進所依賴函數的函數數組int32 NumDependencySingletons; //上述函數的個數uint32 ClassFlags; // EClassFlagsconst FClassFunctionLinkInfo* FunctionLinkArray; // 需要反射的函數,具體的后面會講述int32 NumFunctions; //同上const FPropertyParamsBase* const* PropertyArray; // 后續再講這個int32 NumProperties; //同上const char* ClassConfigNameUTF8;const FCppClassTypeInfoStatic* CppClassInfo;const FImplementedInterfaceParams* ImplementedInterfaceArray; //本例子中沒用上int32 NumImplementedInterfaces; // 0 #if WITH_METADATAconst FMetaDataPairParam* MetaDataArray;int32 NumMetaData; #endif };

以下是ConstructUclass的部分內容。

根據流程圖,可以看到,首先是執行的DependecySingletonFunctions,檢測自己所依賴的單例是否都建立了反射關系。接著就是StaticClass函數部分,這也是我們重點關注的部分。在StaticClass,實現了構建了一個基礎的UClass的反射,但是還沒有完善細節,因此在后續部分給NewClass添加細節部分。

步驟:

執行DependecySingletonFunctions數組中所依賴的函數,確保自身的父類反射已經被構建和自身所在的Package反射被構建。
UObjectForceRegistration,把自己從等待注冊的Map中去掉, 并且注冊。
ConstructUFunction,組建Function,并且注冊Function。(這部分后續再探討
ConstructUProperties,組建Properties。(這部分后續再探討

void ConstructUClass(UClass*& OutClass, const FClassParams& Params) // 關鍵點,用了引用 {if (OutClass && (OutClass->ClassFlags & CLASS_Constructed)) // 如果已經構建過了,則返回{return;}// 執行所依賴過的函數, 需要所依賴部分已經建立反射關系。(PS: Ue4源碼部分 充斥著大量的懶漢單例模式)for (UObject* (*const *SingletonFunc)() = Params.DependencySingletonFuncArray, *(*const *SingletonFuncEnd)() = SingletonFunc + Params.NumDependencySingletons; SingletonFunc != SingletonFuncEnd; ++SingletonFunc){(*SingletonFunc)();}// 所傳進來的是StaticClass函數UClass* NewClass = Params.ClassNoRegisterFunc(); OutClass = NewClass;if (NewClass->ClassFlags & CLASS_Constructed) // 如果這個已經構建過了,返回{return;}// 延遲注冊 // 把自己從等待注冊的Map中去掉UObjectForceRegistration(NewClass); NewClass->ClassFlags |= (EClassFlags)(Params.ClassFlags | CLASS_Constructed);// Make sure the reference token stream is empty since it will be reconstructed later on// This should not apply to intrinsic classes since they emit native references before AssembleReferenceTokenStream is called.if ((NewClass->ClassFlags & CLASS_Intrinsic) != CLASS_Intrinsic){check((NewClass->ClassFlags & CLASS_TokenStreamAssembled) != CLASS_TokenStreamAssembled);NewClass->ReferenceTokenStream.Empty(); #if ENABLE_GC_OBJECT_CHECKSNewClass->DebugTokenMap.Empty(); #endif}// 創建Function的反射,利用傳進來的構建函數來構建函數。NewClass->CreateLinkAndAddChildFunctionsToMap(Params.FunctionLinkArray, Params.NumFunctions);// 如果看過上一篇blog的話,就可以發現其實屬性構建部分是沒有單獨的函數構建的// 原來在這部分實現。ConstructUProperties(NewClass, Params.PropertyArray, Params.NumProperties);// ConstructClass還有很多內容,但是由于篇幅關系,這里就不一一放出來。

注冊階段


注冊階段主要分為兩個過程,首先構建UClass的時候添加進等待注冊表,等待注冊,在收集完成UClass所有信息的時候,延遲注冊。

注冊過程分析
Ue4是如何完成注冊的呢?

/*** Convert a boot-strap registered class into a real one, add to uobject array, etc** @param UClassStaticClass Now that it is known, fill in UClass::StaticClass() as the class*/ void UObjectBase::DeferredRegister(UClass *UClassStaticClass,const TCHAR* PackageName,const TCHAR* InName) {check(Internal::GObjInitialized);// Set object properties.UPackage* Package = CreatePackage(nullptr, PackageName);check(Package);Package->SetPackageFlags(PKG_CompiledIn);OuterPrivate = Package;check(UClassStaticClass);check(!ClassPrivate);ClassPrivate = UClassStaticClass;// Add to the global object table.AddObject(FName(InName), EInternalObjectFlags::None);// Make sure that objects disregarded for GC are part of root set.check(!GUObjectArray.IsDisregardForGC(this) || GUObjectArray.IndexToObject(InternalIndex)->IsRootSet());

其實就是把UClass放到一個Hash表中去。

void HashObject(UObjectBase* Object) {auto& ThreadHash = FUObjectHashTables::Get();Hash = GetObjectHash(Name); ThreadHash.AddToHash(Hash, Object);Hash = GetObjectOuterHash( Name, (PTRINT)Object->GetOuter() );ThreadHash.HashOuter.Add( Hash, Object );AddToOuterMap( ThreadHash, Object );AddToClassMap( ThreadHash, Object ); }

通過上面的代碼,我們可以發現通過名字生成Index和通過OuterName(也就是PackageName)來生成Index,然后添加進Map內部中。具體如何添加進HashMap中呢?請看下面的分析。

TUObjectHashTables
在這里就不得不介紹這個類TUObjectHashTables了,主要的存儲的類。注冊的反射的數據都以存放在這里。
首先看看這個類的定義。

class FUObjectHashTables {FCriticalSection CriticalSection;public:/** Hash sets */TMap<int32, FHashBucket> Hash;TMultiMap<int32, class UObjectBase*> HashOuter;/** Map of object to their outers, used to avoid an object iterator to find such things. **/TMap<UObjectBase*, FHashBucket> ObjectOuterMap;TMap<UClass*, TSet<UObjectBase*> > ClassToObjectListMap;TMap<UClass*, TSet<UClass*> > ClassToChildListMap;static FUObjectHashTables& Get() // 餓漢單例{static FUObjectHashTables Singleton;return Singleton;}FORCEINLINE void AddToHash(int32 InHash, UObjectBase* Object){FHashBucket& Bucket = Hash.FindOrAdd(InHash);Bucket.Add(Object);} }

首先這個類用了餓漢單例的設計模式,保證只有一個存儲數據的實例,

從這個類中我們可以看到有5個Map,具體的映射方式也都顯而易見了,看看我們注冊的時候用了這幾個Map是怎樣處理的。

void HashObject(UObjectBase* Object) {auto& ThreadHash = FUObjectHashTables::Get(); //獲取存儲單例Hash = GetObjectHash(Name); //把FName轉化為Int Index,來映射 ThreadHash.AddToHash(Hash, Object);Hash = GetObjectOuterHash( Name, (PTRINT)Object->GetOuter() );ThreadHash.HashOuter.Add( Hash, Object );AddToOuterMap( ThreadHash, Object );AddToClassMap( ThreadHash, Object ); }

第一個Map:Hash,TMap<int32, FHashBucket> Hash,映射方式通過int32映射到一個桶。(每個桶我們其實都可以看成是一個類的集合)(為了容易理解,此處的int32亦可以理解為FName)

ThreadHash.AddToHash(Hash, Object);FORCEINLINE void AddToHash(int32 InHash, UObjectBase* Object) {FHashBucket& Bucket = Hash.FindOrAdd(InHash);Bucket.Add(Object); }

通過代碼可以看出來,通過自己的名字找到了屬于自己的那個桶,并且在桶中把自己添加進去。

由此可以看出HashMap的用處其實就是通過自身名字找到屬于自己的桶集合。

第二個Map:HashOuter,TMultiMap<int32, class UObjectBase*> HashOuter,映射方式通過int32映射到一個UObjectBase類。(為了容易理解,此處的int32亦可以理解為OuterFName)

ThreadHash.HashOuter.Add( Hash, Object );

通過代碼可以看出,HashOuterMap的用處其實是通過OuterFName的名字來找到自己的Outer。

第三個Map:ObjectOuterMap,TMap<UObjectBase*, FHashBucket> ObjectOuterMap,映射方式是用過Outer來找到自己的Bucket。

AddToOuterMap( ThreadHash, Object );// Assumes that ThreadHash's critical is already locked FORCEINLINE static void AddToOuterMap(FUObjectHashTables& ThreadHash, UObjectBase* Object) {FHashBucket& Bucket = ThreadHash.ObjectOuterMap.FindOrAdd(Object->GetOuter());checkSlow(!Bucket.Contains(Object)); // if it already exists, something is wrong with the external codeBucket.Add(Object); }

通過代碼可以看出, ObjectOuterMap是通過Outer來獲取到Outer的Bucket。(Outer實際上就是Package)(類似于第二個Map)

第四個Map:ClassToObjectListMap,TMap<UClass*, TSet<UObjectBase*> > ClassToObjectListMap。

{check(Object->GetClass());TSet<UObjectBase*>& ObjectList = ThreadHash.ClassToObjectListMap.FindOrAdd(Object->GetClass());bool bIsAlreadyInSetPtr = false;ObjectList.Add(Object, &bIsAlreadyInSetPtr);check(!bIsAlreadyInSetPtr); // if it already exists, something is wrong with the external code }

通過代碼可以看出, ClassToObjectListMap是通過自身來獲取到自身所包含的一個Set(類似于第一個Map)。

第五個Map:ClassToChildListMap,TMap<UClass*, TSet<UClass*> > ClassToChildListMap

UObjectBaseUtility* ObjectWithUtility = static_cast<UObjectBaseUtility*>(Object); if ( ObjectWithUtility->IsA(UClass::StaticClass()) ) {UClass* Class = static_cast<UClass*>(ObjectWithUtility);UClass* SuperClass = Class->GetSuperClass();if ( SuperClass ){TSet<UClass*>& ChildList = ThreadHash.ClassToChildListMap.FindOrAdd(SuperClass);bool bIsAlreadyInSetPtr = false;ChildList.Add(Class, &bIsAlreadyInSetPtr);check(!bIsAlreadyInSetPtr); // if it already exists, something is wrong with the external code} }

總結

以上是生活随笔為你收集整理的UE4反射原理(转)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

www.玖玖玖 | 亚洲激情久久 | 亚洲激情婷婷 | 九九九视频精品 | 在线免费色视频 | 91精品国产一区二区在线观看 | 亚洲精品在线播放视频 | 久久综合五月 | 高清精品视频 | 国产又粗又猛又爽 | 中文字幕最新精品 | 中文字幕成人网 | 天天艹天天爽 | 一本到视频在线观看 | 91麻豆精品国产自产在线游戏 | 中文字幕精品三区 | www.久久久精品 | 亚洲女人天堂成人av在线 | 免费观看十分钟 | 免费黄色小网站 | 日本中文字幕一二区观 | 久久久亚洲精品 | 国产精品1区2区3区在线观看 | www好男人 | 一区二区三区中文字幕在线观看 | av成人黄色 | 九九久久免费 | 在线观看91 | 久热超碰| 国产五码一区 | 黄污在线观看 | 日韩在线观看中文 | 亚洲欧美观看 | 久久夜色精品国产欧美乱 | 黄色精品久久久 | 男女拍拍免费视频 | 99热在线看 | 91在线中文字幕 | 激情导航 | 久久成人精品电影 | 久久短视频 | 99草视频在线观看 | 丁香六月中文字幕 | 亚洲国产精品小视频 | 日韩欧美aaa | 成年人电影毛片 | 婷婷成人亚洲综合国产xv88 | 992tv在线观看网站 | 91视频免费| 啪啪免费观看网站 | 夜色资源网 | 国产美女视频网站 | 五月婷婷在线观看视频 | 国产在线传媒 | 国产欧美久久久精品影院 | 日韩四虎| 99这里只有精品视频 | 天天插天天干天天操 | 视频在线99re | 国产欧美日韩精品一区二区免费 | 久久精品久久久久久久 | 久久国产一二区 | 少妇bbw撒尿 | 一区二区三区电影大全 | 亚洲精品在线免费观看视频 | 精品国产伦一区二区三区观看方式 | 国产精品久久一区二区三区, | 免费看的av片 | 成av人电影 | 欧美aa在线| 久久新 | 91大神精品视频在线观看 | 国产麻豆果冻传媒在线观看 | 国产在线精品区 | 一区中文字幕 | 久草香蕉在线视频 | 欧美一区二区在线看 | av在线免费播放网站 | 99r在线精品 | 黄色aaa级片| 亚洲精品国产品国语在线 | 日韩精品免费在线观看视频 | a天堂一码二码专区 | 黄色一级在线视频 | 国产精品久久久久av福利动漫 | 黄色小说视频在线 | 成人黄色在线电影 | 91av手机在线观看 | 亚洲成av片人久久久 | 国产精品视频专区 | 麻豆视频在线免费观看 | 狠狠色丁香婷综合久久 | 国产精品久久久久久久久毛片 | 日韩一级片大全 | 日韩久久久久久久久久久久 | 国产xx视频 | 综合久久影院 | 国产又粗又猛又黄又爽的视频 | 一级黄色片在线 | 久久99精品久久只有精品 | 在线国产激情视频 | 香蕉视频色 | 丁香花中文字幕 | 中文字幕人成乱码在线观看 | 超级碰99 | 精品视频亚洲 | 亚洲黄色网络 | 黄色av电影网| 日韩欧美视频在线观看免费 | 久久免费精品 | 国产在线精 | 日韩理论在线 | 久久高清视频免费 | 久久久综合香蕉尹人综合网 | 视频二区在线 | 久久成人免费电影 | 九九九九精品 | 国产成人精品综合 | 手机在线黄色网址 | 久久久久亚洲最大xxxx | 在线精品在线 | 中文字幕亚洲欧美日韩2019 | 色多多污污 | 成人免费视频播放 | 久艹视频在线观看 | 亚州天堂 | 视频在线99re | 9999在线 | 午夜精品剧场 | 久久综合干 | 国产精品久久久久久欧美 | 一区二区三区在线视频111 | 五月综合色 | 久久精品4| 天天干天天操天天做 | 视频在线观看入口黄最新永久免费国产 | 日韩欧美视频在线免费观看 | 97av色 | 伊人欧美 | 狠狠操欧美 | 天天拍夜夜拍 | 天天天操天天天干 | www.啪啪.com| 亚洲精品国产欧美在线观看 | 日韩在线免费观看视频 | 成人在线黄色 | 中文字幕av最新更新 | 婷婷综合五月天 | 91九色成人 | 中文字幕高清在线 | 娇妻呻吟一区二区三区 | 天天操天天是 | 亚洲一区久久久 | 成人免费观看视频大全 | 欧美日韩视频一区二区三区 | 99国产一区二区三精品乱码 | 天天爽天天爽 | www.五月婷婷.com | 中文字幕高清有码 | 亚洲精品国产精品久久99 | 综合久色 | 国内精品久久久久影院一蜜桃 | 久久久久激情电影 | 天天爽天天碰狠狠添 | 亚洲精品h | 久久久受www免费人成 | 超碰伊人网 | 99免费看片 | 欧美一区免费观看 | 色婷婷在线视频 | 青青草国产成人99久久 | wwxxxx日本| 久久久久久精 | 精品一区二区三区香蕉蜜桃 | 99国产精品视频免费观看一公开 | 精品一区二区在线免费观看 | 在线免费观看涩涩 | 懂色av一区二区三区蜜臀 | 久久爱资源网 | 五月天,com| 日韩免费在线观看视频 | 在线日韩一区 | 欧美午夜性 | 久久久亚洲麻豆日韩精品一区三区 | 四虎影视国产精品免费久久 | 国精产品一二三线999 | 欧美一级电影在线观看 | 久久精品电影 | 精品视频专区 | 国产精品99久久久久 | 中文字幕乱视频 | 精品av在线播放 | 久久精品视频2 | av三级在线播放 | 91精品国产入口 | 精品视频久久久久久 | 国产亚洲精品久久久久动 | 99九九热只有国产精品 | 欧美日韩亚洲一 | 中文av在线播放 | 91丝袜美腿| 日日碰狠狠躁久久躁综合网 | 中文字幕乱在线伦视频中文字幕乱码在线 | 欧美日韩亚洲在线观看 | 在线观看中文字幕一区 | 久久精品国产免费看久久精品 | 麻豆成人小视频 | 五月色综合 | 成人亚洲欧美 | 中文字幕国产视频 | 亚洲欧美色婷婷 | 国产一级在线 | 久久国精品 | 热久久99这里有精品 | 午夜国产一区 | 91精品久久久久久综合五月天 | 免费观看mv大片高清 | 色综合五月 | 久久久久久久久艹 | 精品久久网 | 精品人人人 | 久久香蕉国产 | 国产视频首页 | 亚洲精品456在线播放第一页 | 日日色综合 | 国产一二区精品 | 国产精品6 | 综合色天天 | 日韩中文字幕亚洲一区二区va在线 | 波多野结衣资源 | 免费日韩视| 精品久久久久国产 | 一区二区三区视频在线 | 波多野结衣一区三区 | 久久精品国产v日韩v亚洲 | 日韩欧美精品在线观看视频 | 久久综合色影院 | 天天色天天操天天爽 | 黄色一级影院 | 91成人精品一区在线播放69 | 国产精品免费一区二区 | 国产 日韩 在线 亚洲 字幕 中文 | 这里只有精彩视频 | 涩涩资源网 | 韩日电影在线免费看 | 一本一道久久a久久精品蜜桃 | 中文字幕免费 | 国产区 在线 | 国产精品女人网站 | 五月婷婷久| 国产成人精品一区二区三区免费 | 在线不卡中文字幕播放 | 国产成人在线网站 | 91桃色在线观看视频 | 国产精品成人在线观看 | 免费网站看v片在线a | 久久国产成人午夜av影院宅 | 中文成人字幕 | 美女久久久久久久久久 | 国产999在线观看 | 久久久精品成人 | 精品视频中文字幕 | 美女免费网视频 | www.操.com| 91完整版在线观看 | 在线观看爱爱视频 | 日韩欧美网址 | 99操视频 | 久久久久久久免费 | av在线免费在线 | 欧美精选一区二区三区 | 婷婷五月情| 中文字幕中文字幕在线一区 | 激情网色| 97精品超碰一区二区三区 | 91手机视频在线 | 久久久久国产成人精品亚洲午夜 | 在线观看你懂的网站 | 一区二区三区播放 | 玖玖玖国产精品 | 婷婷丁香在线观看 | 国产精品久久久久三级 | 97品白浆高清久久久久久 | 在线免费高清视频 | 中文区中文字幕免费看 | 黄色成年片 | 日韩特黄av | 亚洲精品在线观看视频 | 狠色狠色综合久久 | www黄com | 麻花传媒mv免费观看 | 国产天天综合 | 91伊人久久大香线蕉蜜芽人口 | 天天摸天天舔 | 欧美日韩有码 | 久草在线在线视频 | 国内精品久久久久久久影视麻豆 | 国产成人精品不卡 | 日本一区二区免费在线观看 | 日韩中文在线视频 | 99c视频在线| 久久视频这里有精品 | 亚洲黄色一级大片 | 夜夜骑日日操 | 国产精品久久久久亚洲影视 | 91人人澡| 免费久久久| 91在线免费公开视频 | 九九热国产视频 | 国产精品24小时在线观看 | 亚洲精品资源在线观看 | 欧美日韩视频一区二区三区 | 精品久久久久亚洲 | 国产精品大尺度 | 久久久www成人免费毛片麻豆 | 国产精品黄色av | 91av在| 欧美另类69| 欧美一级免费高清 | 国产亚洲欧美一区 | 天天天天天天干 | 成片视频免费观看 | 欧美日韩一区二区三区免费视频 | 欧美在线视频a | 成人黄色大片在线观看 | 在线91色| 免费av高清| 日韩 在线观看 | 美腿丝袜一区二区三区 | 九九九视频在线 | 免费在线成人av | 黄色不卡av | 成人免费xxxxxx视频 | 四虎影视成人精品国库在线观看 | 国产精品久久久久久久久久久久久 | 亚洲伊人第一页 | 99久久久国产精品 | 欧洲精品视频一区二区 | 国产日产精品一区二区三区四区的观看方式 | 欧美怡红院视频 | 深爱五月激情网 | 久久精品一区二区三 | 久久精品一区二区三区中文字幕 | 最新中文在线视频 | 麻豆一区在线观看 | 亚洲精品成人av在线 | 欧美aaa一级 | 中文一二区| 国产福利网站 | 色视频网址| 亚洲精品乱码久久久久久按摩 | av官网 | 欧美日韩在线免费观看视频 | 久久视了 | 日韩精品黄 | 日韩网站一区 | 免费观看国产成人 | 久久人人爽人人爽人人 | 日韩三区在线观看 | 干天天 | 亚洲欧美日韩中文在线 | 激情av资源网 | a v在线观看 | 亚色视频在线观看 | 99久久er热在这里只有精品15 | 3d黄动漫免费看 | 成人丁香花 | 久久精品三 | 在线91播放 | 久久精品国产免费看久久精品 | 久久久精品亚洲 | 久久午夜羞羞影院 | 狠狠操综合网 | 亚洲亚洲精品在线观看 | 黄色av免费电影 | 91久久人澡人人添人人爽欧美 | 一区中文字幕电影 | 国产私拍在线 | 曰本三级在线 | 天天操天天色天天射 | 亚洲视频在线看 | 日韩天天操 | 91精选| 激情五月色播五月 | 久久一区二区三区日韩 | 福利av影院| 国产中文字幕在线观看 | 日日夜日日干 | 亚洲aⅴ一区二区三区 | www看片网站| 国产精品毛片久久蜜 | 国产 在线 日韩 | 一色屋精品视频在线观看 | 日本三级久久 | 日日综合| 激情综合亚洲精品 | 免费看黄色小说的网站 | 欧美视频一区二 | 亚洲一区天堂 | 久草在线在线 | 国产视频九色蝌蚪 | 人人干干人人 | 久久人人爽人人爽人人片av软件 | 欧美日韩裸体免费视频 | 国产成人精品一区二区三区在线 | 91中文在线 | 国产一区在线免费观看 | 亚洲三级精品 | 91精品在线播放 | 精品91在线 | 91av免费看 | 久久亚洲私人国产精品va | 午夜国产福利在线 | 亚洲一区二区三区在线看 | 久久精品视频网站 | 99热超碰在线 | 天天天综合 | 91免费的视频在线播放 | 91麻豆精品国产91久久久久久 | 亚洲成人资源 | 国产999精品久久久影片官网 | 国产午夜精品一区 | 四虎影视精品永久在线观看 | 毛片黄色一级 | 成人综合婷婷国产精品久久免费 | 激情视频二区 | 久久精品一 | 久一网站 | 最新国产在线 | 欧美日韩亚洲在线 | 国产精品久久久久久久久久 | 日韩三级免费 | av网站有哪些 | 91精品视频免费在线观看 | 人人爽人人爱 | 91福利社在线观看 | 国产黄色理论片 | a视频在线播放 | 九九日九九操 | 蜜臀aⅴ精品一区二区三区 久久视屏网 | 成人av影视在线 | 四虎影视精品永久在线观看 | 日韩免费在线观看网站 | 欧美巨大| 婷婷色资源 | 国产一二区免费视频 | 少妇性xxx | 久久免费视频在线观看6 | 深夜国产福利 | 欧美网址在线观看 | 五月婷婷一区二区三区 | 国产免费久久 | 欧美性生活久久 | 国产精久久久久久妇女av | 色视频在线看 | 国产vs久久 | 精品欧美在线视频 | 国产中文字幕91 | 91高清视频在线 | 亚洲五月婷婷 | 日韩视频免费播放 | 麻豆影视在线免费观看 | 这里有精品在线视频 | 五月婷婷视频 | 少妇bbw揉bbb欧美 | 精品国产乱码一区二 | 91九色成人| av免费在线观看网站 | 97视频在线观看成人 | 97视频网址 | 国产在线1区 | 国产视频导航 | 久久精品一区二区三 | 国产99一区二区 | 超碰99人人 | 天天视频色 | 五月激情六月丁香 | 成人在线小视频 | 久久a级片 | 一级特黄aaa大片在线观看 | 日本在线中文 | 麻豆国产精品永久免费视频 | 亚洲精品乱码久久久久久 | 91麻豆产精品久久久久久 | 91视频91自拍 | 午夜av免费观看 | 美女黄视频免费 | 成人a视频片观看免费 | 91av视频在线观看免费 | 成年人视频在线免费 | 日韩精品欧美视频 | 在线播放精品一区二区三区 | 日韩中文字幕视频在线观看 | 成人免费视频网 | 午夜电影中文字幕 | 精品国产自 | 五月婷av| 日本xxxx裸体xxxx17 | 在线观看亚洲精品 | 成人免费色 | 射射射av| 国产成人av电影在线观看 | 夜夜夜夜猛噜噜噜噜噜初音未来 | 亚洲婷婷在线视频 | 亚洲国产网站 | 日本黄色大片儿 | 亚洲精品乱码久久久久久蜜桃动漫 | 久久成年人 | 国产高清不卡在线 | 日韩大陆欧美高清视频区 | 国产中文字幕一区二区三区 | 五月天久久久久 | 日韩有码在线播放 | 婷婷六月激情 | 国产美女精彩久久 | 久久久久麻豆 | 久久99精品久久久久久久久久久久 | 久久99国产综合精品免费 | 午夜12点| 婷婷网站天天婷婷网站 | 丁香久久婷婷 | 天天干一干 | 日韩视频一区二区在线观看 | 日本视频网| 免费精品在线视频 | 狠狠色丁香婷婷综合橹88 | 狠狠色噜噜狠狠 | 国产精品一区二区精品视频免费看 | 久久精品超碰 | 夜夜干夜夜 | 在线观看色网站 | 九九av | 久久综合综合久久综合 | 四虎小视频 | 91在线入口 | 99视频网站 | 精品国产网址 | 亚洲精品视频久久 | av不卡中文字幕 | 中文字幕在线观看第一页 | 日韩理论在线视频 | 九九免费在线观看视频 | 色爱区综合激月婷婷 | 久久精品国产亚洲aⅴ | 精品91在线 | 成人九九视频 | 免费在线观看av网址 | 六月色| 91在线影院| 欧美日韩18 | 中文字幕人成乱码在线观看 | 国产中文字幕一区 | 久久毛片视频 | 不卡的av在线播放 | 五月天婷婷狠狠 | 免费网站污 | 日韩在线观 | 蜜臀av夜夜澡人人爽人人桃色 | 久久电影中文字幕视频 | 久久久久久草 | 天天射色综合 | 国产一区在线播放 | 国产视频2区 | 国产精品手机视频 | 91综合视频在线观看 | 少妇搡bbb| 日韩欧美视频在线播放 | 超碰激情在线 | 免费看污在线观看 | 国产精品美女久久久免费 | 狠狠色丁香久久婷婷综合_中 | 国产亚洲小视频 | 超碰97人人干 | 曰韩精品 | 国产精品ⅴa有声小说 | 日韩视频欧美视频 | 亚洲最大成人网4388xx | 精品欧美小视频在线观看 | 欧美精品久久久久a | av一级免费 | 久草在线电影网 | 久久五月天综合 | 欧美一二区在线 | www.69xx| 99久久久国产精品美女 | 中文字幕在线电影 | 久久免费激情视频 | 中文字幕在线一区观看 | 99re8这里有精品热视频免费 | 狠狠夜夜 | 婷婷丁香六月天 | 青草草在线视频 | 久久国产视频网 | 久久国产欧美日韩 | 很黄很污的视频网站 | 超碰成人免费电影 | 久久九九久久精品 | 免费在线观看av的网站 | 亚洲无吗av | 中文字幕av一区二区三区四区 | 国产成人精品女人久久久 | 日日夜夜天天人人 | 亚洲欧美国产精品va在线观看 | 亚洲激情视频 | 午夜久久影院 | 奇米影视8888在线观看大全免费 | 五月婷婷六月丁香在线观看 | 色婷婷成人 | 日韩在线观看中文字幕 | 午夜av免费看 | av青草| 国产一区二区三区高清播放 | 香蕉精品视频在线观看 | 国产成人精品综合久久久 | 97综合在线| 国产精品自拍在线 | 字幕网资源站中文字幕 | 国产分类视频 | 国产剧情一区在线 | 天天色天天搞 | 色婷婷激情网 | 久久久久久不卡 | 国产一区二区免费在线观看 | 国产精品一区免费在线观看 | 二区在线播放 | 欧美精品在线观看免费 | 久久在线免费视频 | 精品一区二区三区香蕉蜜桃 | 国产麻豆精品免费视频 | 天天干天天操天天爱 | 欧美一区二区三区在线看 | 亚洲国产精品推荐 | 国产伦理剧 | 最近最新最好看中文视频 | 91亚洲网站| 久久久久国产免费免费 | 天天天色综合 | 欧美在线视频不卡 | 亚洲综合成人专区片 | 欧美成人影音 | 婷色在线 | 丁香婷婷色月天 | 成人免费视频网 | 国产aa精品 | 超碰人人99 | 欧美一区二区三区免费看 | 久久精品久久久久电影 | 国产高清在线一区 | 操操操夜夜操 | 在线观看中文字幕第一页 | 91亚洲国产 | www.久久爱.cn| 日韩精品一区二区电影 | 国产视频九色蝌蚪 | 国产日韩精品一区二区三区在线 | 成人黄色片免费看 | 久久99久久99精品免观看软件 | 中文字幕在线观看视频免费 | 国产精品美女视频 | 免费看久久 | 精品免费在线视频 | 成人中心免费视频 | 国产1级视频| 久久黄网站 | 怡红院成人在线 | 一本一本久久a久久 | 在线观看免费高清视频大全追剧 | 久久99精品国产麻豆宅宅 | 精品久久网 | 在线观看av中文字幕 | 99视屏| 日韩大片在线免费观看 | 亚洲免费在线观看视频 | 91成人精品一区在线播放69 | 黄色的视频网站 | 日韩欧美有码在线 | 久久久精品日本 | 国产精品免费观看国产网曝瓜 | 欧美日韩免费在线视频 | 成人午夜精品福利免费 | 久久99久国产精品黄毛片入口 | 国产成人精品久久亚洲高清不卡 | 色国产在线 | av国产在线观看 | 91精品啪在线观看国产线免费 | 色婷婷综合视频在线观看 | 美女禁18| 在线精品视频在线观看高清 | 日日夜夜婷婷 | 成人国产网址 | 亚洲日本va午夜在线影院 | 99精品视频观看 | 天天插天天色 | 国产色就色 | 伊人天天狠天天添日日拍 | 永久免费精品视频 | 91精品久久香蕉国产线看观看 | 国产精品18久久久久vr手机版特色 | 亚洲精品国产精品久久99 | 成年在线观看 | 国产精品中文久久久久久久 | 一区二区三区四区免费视频 | 91精品夜夜 | 日韩欧美网址 | 婷婷久久综合网 | 亚洲国产精品500在线观看 | 国内精品久久久久久久久久 | 国产午夜精品一区二区三区四区 | 97av色| 美女中文字幕 | 天堂av高清 | 国产a视频免费观看 | 亚洲成人av在线播放 | 黄色在线观看免费 | 最新极品jizzhd欧美 | 成人午夜剧场在线观看 | 在线观看韩国av | 激情婷婷网 | 一区视频在线 | 在线观看日本高清mv视频 | 91热视频| 99热这里精品| 午夜精品一区二区三区可下载 | 国产涩图| 久久精品韩国 | 一区二区三区视频在线 | 国产手机在线视频 | av成人免费在线 | 中文字幕在线观看免费观看 | 伊香蕉大综综综合久久啪 | 亚洲资源在线 | 亚洲免费国产视频 | 久久69精品久久久久久久电影好 | 国产中文a | 成人av电影免费在线观看 | 激情久久网 | 国产精品私拍 | 久久精品福利视频 | 美州a亚洲一视本频v色道 | 在线99热 | 欧美日韩视频精品 | 特级西西www44高清大胆图片 | 久久99热这里只有精品国产 | 在线国产视频一区 | 四虎影视av | 午夜黄色一级片 | 欧美一区二区三区免费观看 | 久草.com| 99色视频| 狠狠干狠狠色 | 狠狠色伊人亚洲综合网站色 | 日韩av不卡在线播放 | 国产区免费在线 | 国产精品美女久久久久久久久久久 | 国产一区电影在线观看 | 天天草天天 | 九月婷婷人人澡人人添人人爽 | 精品国产一区二区三区四区在线观看 | 亚洲一区免费在线 | 日产乱码一二三区别免费 | 国内精品久久久久久久影视简单 | 国产成人精品亚洲a | 97在线观看视频 | 日本精油按摩3 | 99精品毛片 | 色免费在线| 日韩精品免费一线在线观看 | 国产精品久久久久久吹潮天美传媒 | 激情六月婷婷久久 | 夜夜躁天天躁很躁波 | 中文字幕亚洲情99在线 | 久久国内视频 | 91av免费看 | 欧美污在线观看 | 天天综合天天综合 | 欧美精品国产精品 | 在线看的av网站 | 91福利视频免费观看 | 美女视频黄在线观看 | 韩国精品一区二区三区六区色诱 | 久久99免费视频 | 久久国产视频网 | 国产精品av免费观看 | 亚州成人av在线 | 国产精品久久中文字幕 | 97超碰在线久草超碰在线观看 | 免费看污污视频的网站 | 黄色片网站av| 国产精品久久伊人 | 日韩视频中文字幕 | 日韩特级黄色片 | 99久久精品国产观看 | 精品一区二区亚洲 | 免费看一级特黄a大片 | 精品一区二区av | 综合av在线 | 91人人揉日日捏人人看 | 成人黄性视频 | 91精品国产91 | wwwwwww色 | 久热色超碰 | 日日日干| 国产又粗又猛又色又黄视频 | 成人在线电影观看 | 视频一区二区三区视频 | 成年人app网址 | 日日爽夜夜操 | 日韩激情视频在线观看 | 欧美精品v国产精品v日韩精品 | 最近中文字幕在线中文高清版 | 精油按摩av | 日韩国产欧美在线视频 | 欧美日韩啪啪 | 亚洲成人精品在线观看 | 亚洲视频aaa | 国产一级特黄毛片在线毛片 | 免费av在线网 | 成人a视频片观看免费 | 中文字字幕在线 | 久草网视频 | 天天操天天操天天操天天操天天操天天操 | 波多野结依在线观看 | 国产精品99在线播放 | 日韩欧美视频 | 久久综合久色欧美综合狠狠 | 99久久99久久免费精品蜜臀 | 天天色宗合 | 欧美日韩国产一二三区 | www.久久色 | 天天干天天弄 | 欧美日韩在线观看不卡 | 国产码电影 | www日韩在线| 四虎影视成人永久免费观看亚洲欧美 | 99热精品在线 | 欧美一区二区三区激情视频 | 91香蕉视频好色先生 | 久久久久久久久久网站 | 国产亚洲综合性久久久影院 | 成人在线黄色 | 狠狠色噜噜狠狠狠狠2022 | 国产成人福利在线观看 | 免费成人在线网站 | 国产在线播放一区二区三区 | 国产成人精品国内自产拍免费看 | 成人羞羞免费 | .国产精品成人自产拍在线观看6 | 国产中文字幕一区二区三区 | 日韩欧美一区二区不卡 | 四虎视频 | 久久精品国产免费看久久精品 | 国产精品国产三级国产aⅴ无密码 | 天天射网 | 亚洲极色| 日韩在线观看视频中文字幕 | 国产成视频在线观看 | 亚洲伦理中文字幕 | 69久久99精品久久久久婷婷 | avhd高清在线谜片 | 久久久麻豆精品一区二区 | 精品综合久久久 | 在线午夜电影神马影院 | 精品久久久久久久久中文字幕 | 国产视频中文字幕 | 国产一区二区播放 | 超碰99在线| 99久久日韩精品免费热麻豆美女 | 麻豆传媒视频在线免费观看 | 狠狠躁日日躁狂躁夜夜躁av | 婷婷六月中文字幕 | 国产精品美女www爽爽爽视频 | 国产精品美女久久久久久免费 | 正在播放五月婷婷狠狠干 | 欧美性色综合网站 | 国产精品免费一区二区 | 久久久免费毛片 | 亚洲丝袜中文 | 日日躁天天躁 | 日韩美在线观看 | 成年人在线观看免费视频 | 亚洲午夜精品久久久久久久久 | 国产一级一片免费播放放 | 狠狠综合| 中文在线a√在线 | 91传媒在线看 | 五月婷婷色播 | 亚洲免费专区 | 亚洲精品国产麻豆 | 亚洲va在线va天堂 | 97网站 | 成人污视频在线观看 | se视频网址 | 欧美日韩激情网 | 日本性xxxxx| 免费合欢视频成人app | 国产精品黄色影片导航在线观看 | 国产精品黄 | 久久少妇免费视频 | 欧美成人在线免费观看 | 久久久久久久久艹 | 国产一区二区三区在线 | 在线观看完整版免费 | 久久久久久久电影 | 天天干夜夜想 | 久草网视频 | 国产精品一区二 | 色av色av色av | 亚洲国产精品va在线看黑人动漫 | 久草在线手机观看 | 一区二区三区中文字幕在线观看 | 国产剧情一区二区在线观看 | 午夜在线观看一区 | 久久久久久久毛片 | 日本 在线 视频 中文 有码 | 国产精品11 | 一区二区久久久久 | 成人午夜电影网 | 天天色天天色天天色 | 波多野结衣最新 | 99热精品免费观看 | 国产视频在线看 | 色综合www| 国产高清视频在线 | 欧美综合在线视频 | 亚洲精品乱码久久久久久蜜桃动漫 | 青青草国产精品 | 国产免费观看av | 天天做天天射 | 婷婷九九| 国产人免费人成免费视频 | 精品嫩模福利一区二区蜜臀 | www在线观看国产 | 久久综合九色综合97_ 久久久 | 国产精品毛片一区二区在线 | 久久人人爽人人爽人人片av软件 | 久久久综合色 | 国产色拍拍拍拍在线精品 | 亚洲欧美精品在线 | 91免费网址 | 久久久久免费观看 | 亚洲国产中文在线 | 伊人影院得得 | 日韩精品一区二区三区在线播放 | 97精品久久 | 成年人av在线播放 | 亚洲va欧美va人人爽春色影视 | 久草在线91 | 欧美男女爱爱视频 | 久久久久成人精品亚洲国产 | 国产亚洲欧美一区 | 国产一区二区在线免费 | 成人免费av电影 | 午夜999 | 韩日三级av| 久久久久久毛片 | 亚洲国产天堂av | 丁香婷婷久久 | 午夜视频在线观看一区二区三区 | 色诱亚洲精品久久久久久 | 成 人 免费 黄 色 视频 | 中文字幕在线有码 | 天天干夜夜擦 | 久久久影院一区二区三区 | 在线v片免费观看视频 | 国产在线高清视频 | av解说在线 | 日韩av影视在线观看 | 色噜噜日韩精品一区二区三区视频 | 91香蕉视频好色先生 | 91丨九色丨91啦蝌蚪老版 | 国产裸体bbb视频 | 亚洲视频在线播放 | 99久久精品电影 | a黄色一级 | 99久久精品免费看 | 精久久久久 | 成人一区在线观看 | 黄色一级免费 | 丁香激情综合久久伊人久久 | 男女精品久久 | 99久久久国产精品免费99 | 欧美激情综合五月色丁香小说 | 国产999精品久久久久久绿帽 | 国产精品国产亚洲精品看不卡15 | 成人免费在线电影 | 国产精品18久久久久久久久 | 日韩在线观看视频中文字幕 | 免费看成人片 | 国产一级淫片免费看 | 国产一级a毛片视频爆浆 | 国产成人亚洲精品自产在线 | 久久观看 | 在线激情电影 | 亚洲精品女人 | 天天曰天天 | 国产精品久久久999 国产91九色视频 | 国产五月婷婷 | 91精品国自产在线偷拍蜜桃 |