TObject简要说明-对象的创建流程
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://www.jiancool.com/article/9141995468/
TObject简要说明-对象的创建流程的更多相关文章
- spring中容器和对象的创建流程
容器和对象的创建流程 1.先创建容器 2.加载配置文件,封装成BeanDefinition 3.调用执行BeanFactoryPostProcessor 准备工作: 准备BeanPostProcess ...
- 【Java基础】Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...
- Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下: package com.test; public ...
- Shiro中Subject对象的创建与绑定流程分析
我们在平常使用Shrio进行身份认证时,经常通过获取Subject 对象中保存的Session.Principal等信息,来获取认证用户的信息,也就是说Shiro会把认证后的用户信息保存在Subjec ...
- 《JavaScript高级程序设计》读书笔记--(4)对象的创建
ECMAScript支持面向对象(OO)编程,但不使用类或者接口.对象可以在代码执行过程中创建或增强,因此具有动态性而非严格定义的实体.在没有类的情况下,可以采用下列模式创建对象. 对象的创建 工厂模 ...
- Delphi 对象的创建(create)与释放(free/destory)
Delphi 对象的创建(create)与释放(free/destory) 1.Create参数为:nil/self/application的区别,最好能看到实际效果的区别 例如: My := TMy ...
- MFC中SDI程序创建流程的回顾
SDI程序创建流程的回顾 从CWinApp.InitialInstance()开始, 1.首先应用程序对象创建文档模板; CSingleDocTemplate* pDocTemplate; pDocT ...
- jQuery对象的创建(一)
在jQuery的常规用法中,执行"$()"返回的是一个jQuery对象,在源码中,它是这样定义的: ... var jQuery = function() { return new ...
- Java对象的创建
学了很久的java,是时候来一波深入思考了.比如:对象是如何在JVM中创建,并且被使用的.本文主要讲解下new对象的创建过程.要想更深入的了解建议去认认真真的看几遍<深入理解Java虚拟机> ...
随机推荐
- 使用jquery 1.7 及以后的版本 attr 问题
跟进jquery的代码进行检查,发现问题出在下面的代码中: if ( notxml ) { name = name.toLowerCase(); hooks = jQuery.attrHooks[ n ...
- Codeforces 486B - OR in Matrix
矩阵的 OR ,也是醉了. 题目意思很简单,就是问你有没有这么一个矩阵,可以变化,得到输入的矩阵. 要求是这个矩阵每行都可以上下任意移动,每列都可以左右任意移动. 解题方法: 1.也是导致我WA 的原 ...
- ZooKeeper原理及配置
ooKeeper实验版本:3.4.6 ZooKeeper下载地址:http://www.apache.org/dyn/closer.cgi/zookeeper/ zookeeper是一个高可用性,高性 ...
- JavaEE Tutorials (3) - 企业bean
3.1什么是企业bean383.1.1企业bean的好处393.1.2何时使用企业bean393.1.3企业bean类型393.2什么是会话bean393.2.1会话bean类型403.2.2何时使用 ...
- HDU 1573 X问题 中国剩余定理
链接:pid=1573">http://acm.hdu.edu.cn/showproblem.php? pid=1573 题意:求在小于等于N的正整数中有多少个X满足:X mod a[ ...
- libvirt学习
高级libvirt API可划分为5个API部分:虚拟机监控程序连接API.域API.网络API.存储卷API.存储池API.
- House Robber 分类: leetcode 算法 2015-07-09 20:53 2人阅读 评论(0) 收藏
DP 对于第i个状态(房子),有两种选择:偷(rob).不偷(not rob) 递推公式为: f(i)=max⎧⎩⎨⎪⎪{f(i−1)+vali,f(i−2)+vali,robi−1==0robi−1 ...
- HDOJ 1005
Input The input consists of multiple test cases. Each test case contains 3 integers A, B and n on a ...
- Android ReceiverCallNotAllowedException: BroadcastReceiver components are not allowed to register to receive intents
ReceiverCallNotAllowedException mContext.registerReceiver()不能在BroadcastReceiver的onReceive()里面调用 可以通过 ...
- 找出并解决 JavaScript 和 Dojo 引起的浏览器内存泄露问题
简介: 如果大量使用 JavaScript 和 Ajax 技术开发 Web 2.0 应用程序,您很有可能会遇到浏览器的内存泄漏问题.如果您有一个单页应用程序或者一个页面要处理很多 UI 操作,问题可能 ...