手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>程序设计>delphi>列表

Delphi 对象模型学习笔记

来源:互联网 作者:西部数码 时间:2008-04-09
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!

TMyObject = class
private
procedure DM1; dynamic;
procedure DM2; dynamic;
procedure WMCommand(var Message); message WM_COMMAND;
public
procedure DM3; dynamic;
end;
凡是带 dynamic 的关键字的方法都会被编译器按照在它们在类中声明的顺序赋予一个编号,或者说 ID,编号用一个 SmallInt 值表示,顺序为 $FFFF,$FFFE,$FFFD...当编译器遇到 message 时,方法的编号则用消息 ID 表示,例如 WM_COMMAND 代表 $0111,有了这些分析我们很容易把这个类的 DMT 按照上面的内存布局描述出来:
4 // Count =4,即包括4个动态方法
$FFFF // DM1 的编号
$FFFE // DM2 的编号
$0111 // WMCommand 的消息 ID
$FFFD // DM3 的编号
DM1 入口地址
DM2 入口地址
WMCommand 入口地址
DM3 入口地址
这些方法是如何被调用的呢?对于带 dynamic 关键字的方法是通过调用 System._CallDynaInst 实现的。通常这样:
mov eax,ebx ; eax = point to current object
mov si,$ffff ; si 为方法编号
call @CallDynaInst
而 System._CallDynaInst 继而会调用 System.GetDynaMethod 获得对应方法编号的方法入口地址,并直接跳到方法入口地址,执行该方法:
procedure _CallDynaInst;
asm
PUSH EAX
PUSH ECX
MOV EAX,[EAX] ;注意调用时传递的是 VMT
CALL GetDynaMethod
POP ECX
POP EAX
JE @@Abstract
JMP ESI ; ESI 指向动态方法入口地址
@@Abstract:
POP ECX
JMP _AbstractError
end;
System.GetDynaMethod 则在 DMT 中寻找该动态方法的编号,如果找到了,就从 DMT 中取得该方法的入口地址;如果找不到,则继续在父类的 DMT 中寻找该方法编号直至 TObject 的 DMT(TObject 中指向 DMT 的指针为 nil,循环到这里自然会停止):
procedure GetDynaMethod;
{ function GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer; }
asm
{ -> EAX vmt of class }
{ SI dynamic method index }
{ <- ESI pointer to routine }
{ ZF = 0 if found }
{ trashes: EAX, ECX }

PUSH EDI
XCHG EAX,ESI
JMP @@haveVMT
@@outerLoop:
MOV ESI,[ESI]
@@haveVMT:
MOV EDI,[ESI].vmtDynamicTable
TEST EDI,EDI
JE @@parent
MOVZX ECX,word ptr [EDI]
PUSH ECX
ADD EDI,2
REPNE SCASW
JE @@found
POP ECX
@@parent:
MOV ESI,[ESI].vmtParent
TEST ESI,ESI
JNE @@outerLoop
JMP @@exit

@@found:
POP EAX
ADD EAX,EAX
SUB EAX,ECX { this will always clear the Z-flag ! }
MOV ESI,[EDI EAX*2-4]

@@exit:
POP EDI
end;
这里多说一句,看 System.GetDynaMethod 的代码简直就像在欣赏一件艺术品,代码被 Borland 的工程师优化得无可挑剔,其中用到了不少技巧,你需要慢慢体会,总之好棒!
至于象 TMyObject 中类似 WMCommand 的方法通常是由 TObject.Dispatch 来进行消息派发的,原理完全同于上面的过程,只是在 DMT 中检索的是消息 ID 而不是方法编号。具体看 TObject.Dispatch 源码及我的《Delphi 的消息机制学习笔记》。

参考文献

1. 李维.《深入核心 -- VCL架构剖析》第二章,2004.1

2. savetime."Delphi 的对象机制浅探", Jan 2004

3. 李战.《悟透delphi》之第二章 --- 《DELPHI的原子世界》

4. Ray Lischner.《Delphi in a Nutshell》,2000

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!