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简要说明-对象的创建流程相关推荐

  1. 面向对象——访问器、修改器、构造函数、对象的创建流程

    在之前的代码讲解中,我们将一类事物封装成一个类,再去创建一个相应的对象去调用方法完成相应的操作这是非常方便的,但是在之前的代码我们发现,对象不仅仅能调用类中的方法,还可以访问到该类中的成员变量并进行赋 ...

  2. Java-Day12 面向对象的三大特征之封装、继承,单例模式(饿汉式、懒汉式)、方法重写 (覆盖)、注解 (annotation)、super关键字、对象的创建流程超详细

    目录 1. 面向对象的三大特征之封装 1.1 封装的含义 1.2 封装的目的 1.3 封装的优点 1.4 封装的实现过程 1.5 拓展知识:JavaBean 2. 面向对象的三大特征之继承 2.1 继 ...

  3. Java 虚拟机学习笔记 | 类加载过程和对象的创建流程

    前言 创建对象是 Java 语言绕不开的话题,那么对象是如何创建出来的呢?我们今天就来聊一聊.对象创建第一步就是检查类是否加载,而类的加载又牵扯到类的加载过程.如果单说对象的创建而绕开类的加载过程,感 ...

  4. Spring的初始化和对象创建流程

    Spring容器的初始化和对象创建流程(代码方面): 如图: 1.Spring容器初始化流程: 文字说明: 在ClassPathXmlApplicationContext的构造函数中: super(p ...

  5. 什么是Spring三级缓存 对象在三级缓存中的创建流程 【三级缓存 循环依赖】

    一.什么是Spring三级缓存 第一级缓存:也叫单例池,存放已经经历了完整生命周期的Bean对象. 第二级缓存:存放早期暴露出来的Bean对象,实例化以后,就把对象放到这个Map中.(Bean可能只经 ...

  6. 韩顺平Java学习笔记P245对象创建流程分析P246this关键字-P250

    P245对象创建流程分析 //1.先在方法区加载方法类 //2.在堆里开空间一个存放age,一个存放name //3.(先默认初始化有默认值age=0,name=null,再显示初始化age=90,n ...

  7. Android系统(245)---SystemServer进程的创建流程

    Android进程系列第三篇---SystemServer进程的创建流程 一.内容预览 SystemServer进程的启动.png 二.概述 前面进程系列已经更新了两篇,本文(基于Android O源 ...

  8. MFC 教程【5_MFC对象的创建】

    MFC对象的创建 前面几章介绍了MFC的核心概念和思想,即介绍了MFC对Windows对象的封装方法和特点:MFC对象的动态创建.序列化:MFC消息映射机制. 现在,考查MFC的应用程序结构体系,即以 ...

  9. android opengl流程,【Android OpenGL ES】Android Opengl ES创建流程

    在android 1.0rc2 sdk中,提供了以下包支持Opengl ES 编程: 一.openglES包 android.opengl Class: GLDebugHelper:用于调试OpenG ...

最新文章

  1. pandas 数据分析使用
  2. 发布会预告 | 多账号管控与网络安全集中化管理能力升级
  3. python画五角星代码_008-python绘制五个五角星
  4. python数组替换_Python:替换数组中的值
  5. Fortinet不断践行安全平台的战略保持长效增长
  6. NodeJS同步MySQL上游数据到ElasticSearch数据库中
  7. 编写python程序、输出*图形_Python用程序输出字母“C”的图案
  8. JavaScript进制之间的转换
  9. Linux Shell变量使用
  10. 个人博客存在的三种形式
  11. OriginPro 2021 设置成中文(软件自带)
  12. 【Axure】Axure RP制作日期控件
  13. 梯度下降法的不同形式——随机梯度下降法和小批量梯度下降法
  14. Oracle DUL的工作原理和技术实现
  15. np.meshgrid()函数
  16. 页面分享到微博、qq、qqzone
  17. 被误以为外国佬开发的4款国产软件,早年默默无闻,如今声名鹊起
  18. 自己立的flag,倒了也要扶起来
  19. antv 官方文档参考 ywy(基础)
  20. hiho1550 顺序三元组

热门文章

  1. 8工具来优化你的图像
  2. 重设忘记的Mysql密码
  3. hdu 1498(二分图最小顶点覆盖)
  4. VMware安装Linux ubuntu虚拟机
  5. NYOJ 1076 方案数量(公式 或 递推)
  6. c#中获取服务器IP,客户端IP以及Request.ServerVariables详细说明
  7. 修改Linux系统时间
  8. linux常用命令:sudo 命令
  9. Java学习:多线程(2)
  10. CodeForces - 434D Nanami's Power Plant