TObject简要说明-对象的创建流程
生活随笔
收集整理的這篇文章主要介紹了
TObject简要说明-对象的创建流程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Delphi:TObject簡要說明-對象的創建流程2009-08-14 08:57TObject = class //創建 constructor Create; //釋放 procedure Free; //初始化實列 class function InitInstance(Instance: Pointer): TObject; //清除實列 procedure CleanupInstance; //獲得類的類型 function ClassType: TClass; //獲得了的名稱 class function ClassName: ShortString; //判斷類的名稱 class function ClassNameIs(const Name: string): Boolean; //類的父類 class function ClassParent: TClass; //類的信息指針 class function ClassInfo: Pointer; //當前類的實列大小 class function InstanceSize: Longint; //判斷是否從一個類繼承下來 class function InheritsFrom(AClass: TClass): Boolean; //根據方法的名稱獲得方法的地址 class function MethodAddress(const Name: ShortString): Pointer; //根據地址或的方法的名稱 class function MethodName(Address: Pointer): ShortString; //根據名稱獲得屬性的地址 function FieldAddress(const Name: ShortString): Pointer; //查詢接口 function GetInterface(const IID: TGUID; out Obj): Boolean; //獲得接口的入口 class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; //獲得接口表 class function GetInterfaceTable: PInterfaceTable; //安全調用例外 function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; virtual; //創建之后的執行 procedure AfterConstruction; virtual; //釋放之前的執行 procedure BeforeDestruction; virtual; //分派消息 procedure Dispatch(var Message); virtual; //默認的句柄 procedure DefaultHandler(var Message); virtual; //新的實列 class function NewInstance: TObject; virtual; //釋放實列 procedure FreeInstance; virtual; //釋放 destructor Destroy; virtual; end; //初始化實列 class function TObject.InitInstance(Instance: Pointer): TObject; {$IFDEF PUREPASCAL} var IntfTable: PInterfaceTable; ClassPtr: TClass; I: Integer; begin //分配需要的內存的大小 FillChar(Instance^, InstanceSize, 0); //實列化分配好的內存 PInteger(Instance)^ := Integer(Self); ClassPtr := Self; //如果成功 while ClassPtr <> nil do begin //獲得接口表 IntfTable := ClassPtr.GetInterfaceTable; //遍歷接口 if IntfTable <> nil then for I := 0 to IntfTable.EntryCount-1 do //初始化每個接口函數的具體實現 with IntfTable.Entries[I] do begin if VTable <> nil then PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable); end; ClassPtr := ClassPtr.ClassParent; end; Result := Instance; end; //清除實列 procedure TObject.CleanupInstance; {$IFDEF PUREPASCAL} var ClassPtr: TClass; InitTable: Pointer; begin //獲得當前的類型 ClassPtr := ClassType; //獲得初始化標的地址 InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^; //如果當前類存在 并且初始化表也存在 while (ClassPtr <> nil) and (InitTable <> nil) do begin //釋放所有的信息 _FinalizeRecord(Self, InitTable); //如果當前類有父類 則清楚父類的信息 ClassPtr := ClassPtr.ClassParent; if ClassPtr <> nil then InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^; end; end; //獲得當前類的類型 function TObject.ClassType: TClass; begin //就是返回當前類的指針 Pointer(Result) := PPointer(Self)^; end; //獲得當前類的類名 class function TObject.ClassName: ShortString; {$IFDEF PUREPASCAL} begin //根據虛擬方發表返回指定的地址 Result := PShortString(PPointer(Integer(Self) + vmtClassName)^)^; end; // 判斷當前類的類名 class function TObject.ClassNameIs(const Name: string): Boolean; {$IFDEF PUREPASCAL} var Temp: ShortString; I: Byte; begin Result := False; //獲得當前類的類名得指針 Temp := ClassName; //根據字符串的長度比較每個字符 區分大小寫 for I := 0 to Byte(Temp[0]) do if Temp[I] <> Name[I] then Exit; Result := True; end; //獲得當前類的父類 class function TObject.ClassParent: TClass; {$IFDEF PUREPASCAL} begin //根據虛擬方法表或的父的地址指針 Pointer(Result) := PPointer(Integer(Self) + vmtParent)^; //如果存在父類 則返回 if Result <> nil then Pointer(Result) := PPointer(Result)^; end; {$ELSE} asm MOV EAX,[EAX].vmtParent TEST EAX,EAX JE @@exit MOV EAX,[EAX] @@exit: end; //獲得類型信息 class function TObject.ClassInfo: Pointer; begin Result := PPointer(Integer(Self) + vmtTypeInfo)^; end; //獲得實列大小 class function TObject.InstanceSize: Longint; begin Result := PInteger(Integer(Self) + vmtInstanceSize)^; end; //判斷是否從一個類繼承下來 class function TObject.InheritsFrom(AClass: TClass): Boolean; {$IFDEF PUREPASCAL} var ClassPtr: TClass; begin ClassPtr := Self; //當前類是否存在 并且和比較的類不等 while (ClassPtr <> nil) and (ClassPtr <> AClass) do //獲得這個類的父類 ClassPtr := PPointer(Integer(ClassPtr) + vmtParent)^; Result := ClassPtr = AClass; end; {$ELSE} asm { -> EAX Pointer to our class } { EDX Pointer to AClass } { <- AL Boolean result } JMP @@haveVMT @@loop: MOV EAX,[EAX] @@haveVMT: CMP EAX,EDX JE @@success MOV EAX,[EAX].vmtParent TEST EAX,EAX JNE @@loop JMP @@exit @@success: MOV AL,1 @@exit: end; //根據方法名稱獲得地址 class function TObject.MethodAddress(const Name: ShortString): Pointer; asm { -> EAX Pointer to class } { EDX Pointer to name } PUSH EBX PUSH ESI PUSH EDI XOR ECX,ECX //清零 XOR EDI,EDI //清零 MOV BL,[EDX] //獲得字符串的長度 JMP @@haveVMT //判斷是否有虛擬方發表 @@outer: { upper 16 bits of ECX are 0 ! } MOV EAX,[EAX] @@haveVMT: MOV ESI,[EAX].vmtMethodTable //獲得虛擬方發表的地址 TEST ESI,ESI //是否存在 JE @@parent //如果不存在 MOV DI,[ESI] { EDI := method count }方法的數量 ADD ESI,2 // 開始 @@inner: { upper 16 bits of ECX are 0 ! } MOV CL,[ESI+6] { compare length of strings } //獲得名城的長度 CMP CL,BL //比較長度 JE @@cmpChar //如果相等就開始比較字符 @@cont: { upper 16 bits of ECX are 0 ! } MOV CX,[ESI] { fetch length of method desc } //獲得方法的長度 //長度兩個字節 指針4個字節 /// ADD ESI,ECX { point ESI to next method } //指向下一個函數 DEC EDI JNZ @@inner @@parent: //獲得父的方發表 MOV EAX,[EAX].vmtParent { fetch parent vmt } TEST EAX,EAX //是否為0 JNE @@outer //不為零 JMP @@exit { return NIL } //已經到根 @@notEqual: MOV BL,[EDX] { restore BL to length of name } //存儲名字的長度 JMP @@cont //轉移 @@cmpChar: { upper 16 bits of ECX are 0 ! } MOV CH,0 { upper 24 bits of ECX are 0 ! } ///清空高位字節 @@cmpCharLoop: MOV BL,[ESI+ECX+6] { case insensitive string cmp } //獲得第一個字符 XOR BL,[EDX+ECX+0] { last char is compared first } //比較 AND BL,$DF //清空其他標志位 JNE @@notEqual DEC ECX { ECX serves as counter } //比較下一個 JNZ @@cmpCharLoop //如果不為零 進行下一個字符的比較 { found it } MOV EAX,[ESI+2] //找到 并且得到指針 12 方法長度 3456 方法指針 7890 方法名稱 7 方法名城的長度 @@exit: POP EDI POP ESI POP EBX end; //根據字段名獲得地址 function TObject.FieldAddress(const Name: ShortString): Pointer; asm { -> EAX Pointer to instance } { EDX Pointer to name } PUSH EBX PUSH ESI PUSH EDI XOR ECX,ECX //清空Cx XOR EDI,EDI //清空Edit MOV BL,[EDX] //獲得Name的長度 PUSH EAX { save instance pointer } //保存當前實列指針 @@outer: MOV EAX,[EAX] { fetch class pointer } //獲得當前類的指針 MOV ESI,[EAX].vmtFieldTable //獲得字段列表的地址 TEST ESI,ESI //是否存在 JE @@parent //如果不存在就到當前的父類查找 MOV DI,[ESI] { fetch count of fields } //獲得字段的數量 ADD ESI,6 // 2 為數量 4 位指針 @@inner: MOV CL,[ESI+6] { compare string lengths } //獲得當前字段的長度 CMP CL,BL //比較長度 JE @@cmpChar //如果相等 就開始比較 字符 @@cont: ///LEA是取變量的地址 LEA ESI,[ESI+ECX+7] { point ESI to next field } //Esi指向下一個字段ESI 當前位子+ECX 長度+7 ??? DEC EDI //數量減一 JNZ @@inner //如果不等于零則繼續比較 @@parent: MOV EAX,[EAX].vmtParent { fetch parent VMT } //獲得當前的父類地址 TEST EAX,EAX //是否存在 JNE @@outer //如果存在則準備獲得字段數量 POP EDX { forget instance, return Nil } //否則恢復Edx 恢復實列 返回nil 當前Eax為空 JMP @@exit //并且退出 @@notEqual: MOV BL,[EDX] { restore BL to length of name } //獲得目的字段名稱的長度 MOV CL,[ESI+6] { ECX := length of field name } //獲得源字段名城的長度 JMP @@cont @@cmpChar: MOV BL,[ESI+ECX+6] { case insensitive string cmp } //字符比較 XOR BL,[EDX+ECX+0] { starting with last char } AND BL,$DF //標志位處理 JNE @@notEqual //如果不等 DEC ECX { ECX serves as counter } //字符長度減一 JNZ @@cmpChar //如果還有沒有比較完的字符 { found it } MOV EAX,[ESI] { result is field offset plus ... } //獲得當前的地址的偏移量 POP EDX //恢復當前實列到Edx ADD EAX,EDX { instance pointer } //獲得字段的偏移地址 @@exit: POP EDI POP ESI POP EBX end; // function TObject.GetInterface(const IID: TGUID; out Obj): Boolean; var InterfaceEntry: PInterfaceEntry; begin Pointer(Obj) := nil; InterfaceEntry := GetInterfaceEntry(IID); if InterfaceEntry <> nil then begin if InterfaceEntry^.IOffset <> 0 then begin Pointer(Obj) := Pointer(Integer(Self) + InterfaceEntry^.IOffset); if Pointer(Obj) <> nil then IInterface(Obj)._AddRef; end else IInterface(Obj) := InvokeImplGetter(Self, InterfaceEntry^.ImplGetter); end; Result := Pointer(Obj) <> nil; end; ---------------------- 一個實列的創建過程 s:=Tstrings.create ; Mov Dl ,$01, Mov Eax , [$00412564]; //?? Call Tobject.create ; { Test dl,dl ; Jz +$08 ///??? Add Esp,-$10; Call @ClassCreate; { push Edx, Push Ecx, Push Ebx, Test Dl,dl jl +03 Call Dword Ptr[eax-$0c] { NewInStance push Ebx mov Ebx ,eax mov Eax ,ebx Call Tobject.instancesize { Add Eax,-$28 Mov Eax,[Eax] Ret } Call @GetMem { push Ebx Test Eax,Eax jle +$15 Call Dword ptr [memoryManager] Mov Ebx,Eax Test Ebx,ebx Jnz +$0B mov Al,%01 Call Error Xor Ebx,Ebx pop Ebx Ret } mov Edx,Eax Mov Eax,Ebx, call Tobject.initInstance pop Ebx } Xor Edx,edx Lea Ecx,[Esp+$10] Mov Ebx,Fs:[Edx] mov [Ecx],EDx mov [Ecx+$08],ebx mov [Ecx+$04],$0040340D mov Fs:[Edx] , Ecx pop Ebx pop Ecx pop Edx } } Test dl,dl, jz +0f Call @AfterConStruction pop Dword ptr Fs:[$00000000] Add Esp ,$0c } 一個類實例的生成需要經過對象內存分配、內存初始化、設置對象執行框架三個步驟。 編譯器首先調用 System._ClassCreate 進行對象內存分配、內存初始化的工作。而 System._ClassCreate 調用 TObject 類的虛方法 NewInstance 建立對象的實例空間,繼承類通常不需要重載 TObject.NewInstance,除非你使用自己的內存管理器,因此缺省是調用 TObject.NewInstance。TObject.NewInstance 方法將根據編譯器在類信息數據中初始化的對象實例尺寸(TObject.InstanceSize),調用系統缺省的 MemoryManager.GetMem 過程為該對象在堆(Heap)中分配內存,然后調用 TObject.InitInstance 方法將分配的空間初始化。InitInstance 方法首先將對象空間的頭4個字節初始化為指向對象類的 VMT 的指針,然后將其余的空間清零。如果類中還設計了接口,它還要初始化接口表格(Interface Table)。 當對象實例在內存中分配且初始化后,開始設置執行框架。所謂設置執行框架就是執行你在 Create 方法里真正寫的代碼。設置執行框架的規矩是先設置基類的框架,然后再設置繼承類的,通常用 Inherited 關鍵字來實現。 上述工作都做完后,編譯器還要調用 System._AfterConstruction 讓你有最后一次機會進行一些事務的處理工作。System._AfterConstruction 是調用虛方法 AfterConstruction 實現的。 在 TObject 中 AfterConstruction 中只是個 Place Holder,你很少需要重載這個方法,重載這個方法通常只是為了與 C++ Builder 對象模型兼容。 最后,編譯器返回對象實例數據的地址指針。 對象釋放服務其實就是對象創建服務的逆過程,可以認為對象釋放服務就是回收對象在創建過程中分配的資源。 當編譯器遇到 destructor 關鍵字通常會這樣編碼:首先調用 System._BeforeDestruction,而 System._BeforeDestruction 繼而調用虛方法 BeforeDestruction, 在 TObject 中 BeforeDestruction 中只是個 Place Holder,你很少需要重載這個方法,重載這個方法通常只是為了與 C++ Builder 對象模型兼容。 這之后,編譯器調用你在 Destroy 中真正寫的代碼,如果當前你在撰寫的類是繼承鏈上的一員,不要忘記通過 inherited 調用父類的析構函數以釋放父類分配的資源,但規矩是,先釋放當前類的資源,然后再調用父類的,這和對象創建服務中設置對象執行框架的順序恰好相反。 當前類及繼承鏈中所有類中分配的資源全部釋放后,最后執行的就是釋放掉對象本身及一些特別數據類型占用的內存空間。編譯器調用 System._ClassDestroy 來完成這件工作。System._ClassDestroy 繼而調用虛方法 FreeInstance,繼承類通常不需要重載 TObject.FreeInstance,除非你使用自己的內存管理器,因此缺省是調用 TObject.FreeInstance。TObject.FreeInstance 繼而調用 TObject.CleanupInstance 完成對于字符串數組、寬字符串數組、Variant、未定義類型數組、記錄、接口和動態數組這些特別數據類型占用資源的釋放[4],最后 TObject.FreeInstance 調用 MemoryManager.FreeMem 釋放對象本身占用的內存空間。 很有意思的是,對象釋放服務與對象創建服務所用方法、函數是一一對應的,是不是有一種很整齊的感覺? 對象創建服務 對象釋放服務 System._ClassCreate System._ClassDestroy System._AfterConstruction System._BeforeDestruction TObject.AfterConstruction(virtual) TObject.BeforeDestruction(virtual) TObject.NewInstance(virtual) TObject.FreeInstance(virtual) TObject.InitInstance TObject.CleanupInstance MemoryManager.GetMem MemoryManager.FreeMem 還有一點要注意,通常我們不會直接調用 Destroy 來釋放對象,而是調用 TObject.Free,它會在釋放對象之前檢查對象引用是否為 nil。
總結
以上是生活随笔為你收集整理的TObject简要说明-对象的创建流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【需求工程】需求获取
- 下一篇: 作者:刘大伟(1984-),男,博士,烟