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}
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。

http://blog.csdn.net/zang141588761/article/details/51993900

Tobject 类解析的更多相关文章

  1. Thrift compiler代码生成类解析

    代码生成类解析: Thrift--facebook RPC框架,介绍就不说了,百度,google一大把,使用也不介绍,直接上结构和分析吧. Hello.thrift文件内容如下: namespace ...

  2. Delphi中的Sender:TObject对象解析

    Delphi中的Sender:TObject对象解析 procedure TForm1.Button1Click(Sender: TObject); begin end; 解析:Procedure是过 ...

  3. Delphi中的Sender:TObject对象解析转载

    https://blog.csdn.net/jl_tiny/article/details/24376661 Delphi中的Sender:TObject对象解析 procedure TForm1.B ...

  4. Bootstrap 类解析

    Bootstrap 类解析 元素 Bootstrap 类 定义 <div> container 内容容器 <table> table 表格 <table> tabl ...

  5. 【Owin 学习系列】2. Owin Startup 类解析

    Owin Startup 类解析 每个 Owin 程序都有 startup 类,在这个 startup 类里面你可以指定应用程序管道模型中的组件.你可以通过不同的方式来连接你的 startup 类和运 ...

  6. SpringBoot入门(三)——入口类解析

    本文来自网易云社区 上一篇介绍了起步依赖,这篇我们先来看下SpringBoot项目是如何启动的. 入口类 再次观察工程的Maven配置文件,可以看到工程的默认打包方式是jar格式的. <pack ...

  7. Spark 资源调度包 stage 类解析

    spark 资源调度包 Stage(阶段) 类解析 Stage 概念 Spark 任务会根据 RDD 之间的依赖关系, 形成一个DAG有向无环图, DAG会被提交给DAGScheduler, DAGS ...

  8. 【Spring注解驱动开发】AOP核心类解析,这是最全的一篇了!!

    写在前面 昨天二狗子让我给他讲@EnableAspectJAutoProxy注解,讲到AnnotationAwareAspectJAutoProxyCreator类的源码时,二狗子消化不了了.这不,今 ...

  9. 【Python】-【类解析】--【脚本实例】

    通过脚本事例,解析下Python中类的几个概念在脚本中的应用 脚本如下: ++++++++++++++++++++++++++++++++++++++++ #!/usr/bin/env python# ...

随机推荐

  1. 【BZOJ 1011】[HNOI2008]遥远的行星

    [题目链接]:http://www.lydsy.com/JudgeOnline/problem.php?id=1011 [题意] [题解] 这里的答案误差不超过5%是突破点; 如果是直接暴力写; 复杂 ...

  2. No USB devices or running emulators detected”

    每次重装系统之后,安装andorid studio后,使用真机调试代码,就会出现"No USB devices or running emulators detected"的错误, ...

  3. FreeBSD 内核中的SYSINIT分析【转】

    FreeBSD kernel是一个膨大的系统, 对于这样一个大系统, 里面往往包含了大量的子系统和  模块,当系统初始化时这些模块就需要初始化, 按照通常的思路,这些初始化过程必须在某处  被显式地调 ...

  4. Mechanism for self refresh during C0

    An embodiment may be an apparatus comprising a link coupled with a memory, and circuitry coupled wit ...

  5. Oracle 学习笔记 18 -- 存储函数和存储过程(PL/SQL子程序)

    PL/SQL子程序 它包含了函数和过程.此功能是指用户定义的函数.和系统功能是不同的.子程序通常完成特定的功能PL/SQL座.,能够被不同的应用程序多次调用.Oracle提供能够把PL/SQL程序存储 ...

  6. 监听WPF依赖属性

    原文:监听WPF依赖属性 当我们使用依赖属性的时候,有时需要监听它的变化,这在写自定义控件的时候十分有用, 下面介绍一种简单的方法.   如下使用DependencyPropertyDescripto ...

  7. hadoop学习笔记(四)——eclipse+maven+hadoop2.5.2源代码

    Eclipse同maven进口hadoop源代码 1)  安装和配置maven环境变量 M2_HOME: D:\profession\hadoop\apache-maven-3.3.3 PATH: % ...

  8. c#开发移动APP-Xamarin入门扩展剖析

    原文:c#开发移动APP-Xamarin入门扩展剖析 上节将Phoneword应用程序扩展到包含第二个屏幕,该屏幕可以跟踪应用程序的拨打历史 Navigation Xamarin.Form提供了一个内 ...

  9. wpf实现两头渐窄的线条效果

    原文:wpf实现两头渐窄的线条效果 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/huangli321456/article/details/800 ...

  10. [转]完美解决)Tomcat启动提示At least one JAR was scanned for TLDs yet contained no TLDs

    一.文章前言    本文是亲测有效解决At least one JAR was scanned for TLDs yet contained no TLDs问题,绝对不是为了积分随便粘贴复制然后压根都 ...