昨天把MakeObjectInstance的代码详细研究了一下,当然还有众多前辈高手们的帮助,终于大致搞明白了是怎么回事。但是给我顿悟的,不是高手们的帖子,而是来自我自己的一个疑惑,TObjectInstance这么小,一共才13个字节,显然不可能存储整个MainWndProc的函数内容,更不用说WndProc函数的内容,而只能存储它们的地址。也不可能把窗口函数的内容拆散了放在不同的TObjectInstance里,那样虽然可以,但是何不把TObjectInstance设计的大一些呢,设计VCL代码的都是高手中的高手,不可能犯这样的低级错误。正是从这一点出发,我明白了,MakeObjectInstance函数不过是换种方式调用MainWndProc,而不是把MainWndProc的内容整个存储在MakeObjectInstance建立的区块里并直接执行它的汇编指令(过去我一直以来就是这么认为的),每个TObjectInstance小区块最终记住的是每个Delphi类(比如TButton)的MainWndProc函数地址,外加跳转语句。
与之而来的推论是,TInstanceBlock建立了314个小区块TObjectInstance的列表,第一个小区块存储了Application.WndProc的地址(不是虚函数,如果变成虚函数就不行了),第二个小区块存储了Form1.MainWndProc的地址,如果Form1上只有2个TButton,那么第三个区块存储了Button1.MainWndProc的地址,第四个区块存储了Button2.MainWndProc的地址,以此类推。一般情况下,程序员用的了314那么多带回调函数的GUI实例吗?好像不需要,而且只有界面Win控件(图像控件虽有WndProc"窗口函数",但它是通过Win控件转发执行的,所以仍然不需要。理论是这样,做例子验证了也是这样)才需要这个。万一超过了也不要紧,Delphi还会给我们建立下一个314列表。我不明白的是,这么浅显的结论,高手们为什么不明确说出来呢,像我这样举个例子多么容易理解啊,代码都不用看就可以懂的,真是郁闷,这么多年都高看了MakeObjectInstance的神秘之处,其实挺简单嘛!
----------------------------------------------------------------------------
做个实验:
把TApplication.FObjectInstance和TWinControl.FObjectInstance移到public区域,然后新建一个工程,上面只放2个TButton。输入以下代码(放在Button1和Button2里没有区别)
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(Integer(Application.FObjectInstance)));
ShowMessage(IntToStr(Integer(Form1.FObjectInstance)));
ShowMessage(IntToStr(Integer(Button1.FObjectInstance)));
ShowMessage(IntToStr(Integer(Button2.FObjectInstance)));
end;
因为TInstanceBlock里的314个TObjectInstance是倒着用的,所以最后一个TObjectInstance的地址值最大,但是第一个FObjectInstance存储在这里(当然也就是Application的)。然后就可以观察输出值了:
4067311 (Application)
4067285 (Form1),减小了26,不知道为什么是相差2个TObjectInstance的距离?经过调试,发现是有一个THintWindow实例在Form1之前就占据了一个位置。
4067272 (Button1),减小13,正确
4067259 (Button2),减小13,正确
值得注意的是,如果把dfm文件里的Button1和Button2顺序颠倒,那么根据Delphi编译器实例化控件的顺序,会先实例化Button2,后实例化Button1,打印语句不变,输出结果就变成这样:
4067311 (Application)
4067285 (Form1)
4067259 (Button1),数值较小
4067272 (Button2),数值较大,此时Button2比Button1先实例化,因此也先占据一个TObjectInstance,地址值更大(倒着用TObjectInstance链表)
----------------------------------------------------------------------------
FHintWindow实例化的过程:
Controls单元初始化
initialization
InitControls;
调用
procedure InitControls;
begin
Screen := TScreen.Create(nil);
Application := TApplication.Create(nil);
Application.ShowHint := True; // 类属性
end;
类属性调用SetShowHint,就当场创建了一个FHintWindow,比Form1还要快
procedure TApplication.SetShowHint(Value: Boolean);
begin
if FShowHint <> Value then
begin
FShowHint := Value;
if FShowHint then
begin
FHintWindow := HintWindowClass.Create(Self);
FHintWindow.Color := FHintColor;
end else
begin
FHintWindow.Free;
FHintWindow := nil;
end;
end;
end;
----------------------------------------------------------------------------
参考:
http://blog.csdn.net/linzhengqun/article/details/1451088
http://www.wenhq.com/article/view_48.html
http://blog.csdn.net/sforiz/article/details/8057371