原链接:
TObject = 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}varIntfTable: PInterfaceTable;ClassPtr: TClass;I: Integer;begin//分配需要的内存的大小FillChar(Instance^, InstanceSize, 0);//实列化分配好的内存PInteger(Instance)^ := Integer(Self);ClassPtr := Self;//如果成功while ClassPtr <> nil dobegin //获得接口表 IntfTable := ClassPtr.GetInterfaceTable; //遍历接口 if IntfTable <> nil then for I := 0 to IntfTable.EntryCount-1 do//初始化每个接口函数的具体实现with IntfTable.Entries[I] dobegin if VTable <> nil then PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable);end; ClassPtr := ClassPtr.ClassParent;end;Result := Instance;end;//清除实列
procedure TObject.CleanupInstance;{$IFDEF PUREPASCAL}varClassPtr: TClass;InitTable: Pointer;begin//获得当前的类型ClassPtr := ClassType;//获得初始化标的地址 InitTable := PPointer(Integer(ClassPtr) + vmtInitTable)^;//如果当前类存在 并且初始化表也存在while (ClassPtr <> nil) and (InitTable <> nil) dobegin //释放所有的信息 _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}varTemp: ShortString;I: Byte;beginResult := 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;beginResult := PPointer(Integer(Self) + vmtTypeInfo)^;end;//获得实列大小
class function TObject.InstanceSize: Longint;beginResult := PInteger(Integer(Self) + vmtInstanceSize)^;end;//判断是否从一个类继承下来
class function TObject.InheritsFrom(AClass: TClass): Boolean;{$IFDEF PUREPASCAL}varClassPtr: TClass;beginClassPtr := 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 EBXend;//根据字段名获得地址
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 EBXend; //function TObject.GetInterface(const IID: TGUID; out Obj): Boolean;
varInterfaceEntry: PInterfaceEntry;beginPointer(Obj) := nil;InterfaceEntry := GetInterfaceEntry(IID);if InterfaceEntry <> nil thenbegin 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._ClassCreateSystem._ClassDestroySystem._AfterConstructionSystem._BeforeDestructionTObject.AfterConstruction(virtual)TObject.BeforeDestruction(virtual)TObject.NewInstance(virtual)TObject.FreeInstance(virtual)TObject.InitInstanceTObject.CleanupInstanceMemoryManager.GetMemMemoryManager.FreeMem 还有一点要注意,通常我们不会直接调用 Destroy 来释放对象,而是调用 TObject.Free,它会在释放对象之前检查对象引用是否为 nil。