昨天把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

MakeObjectInstance的简单理解的更多相关文章

  1. git的简单理解及基础操作命令

    前端小白一枚,最近开始使用git,于是花了2天看了廖雪峰的git教程(偏实践,对于学习git的基础操作很有帮助哦),也在看<git版本控制管理>这本书(偏理论,内容完善,很不错),针对所学 ...

  2. 简单理解Struts2中拦截器与过滤器的区别及执行顺序

    简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...

  3. [转]简单理解Socket

    简单理解Socket 转自 http://www.cnblogs.com/dolphinX/p/3460545.html  题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公 ...

  4. Js 职责链模式 简单理解

    js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...

  5. Deep learning:四十六(DropConnect简单理解)

    和maxout(maxout简单理解)一样,DropConnect也是在ICML2013上发表的,同样也是为了提高Deep Network的泛化能力的,两者都号称是对Dropout(Dropout简单 ...

  6. Deep learning:四十二(Denoise Autoencoder简单理解)

    前言: 当采用无监督的方法分层预训练深度网络的权值时,为了学习到较鲁棒的特征,可以在网络的可视层(即数据的输入层)引入随机噪声,这种方法称为Denoise Autoencoder(简称dAE),由Be ...

  7. 简单理解dropout

    dropout是CNN(卷积神经网络)中的一个trick,能防止过拟合. 关于dropout的详细内容,还是看论文原文好了: Hinton, G. E., et al. (2012). "I ...

  8. 我们为之奋斗过的C#-----C#的一个简单理解

    我们首先来简单叙述一下什么是.NET,以及C#的一个简单理解和他们俩的一个区别. 1 .NET概述 .NET是Microsoft.NET的简称,是基于Windows平台的一种技术.它包含了能在.NET ...

  9. 简单理解ECMAScript2015中的箭头函数新特性

    箭头函数(Arrow functions),是ECMAScript2015中新加的特性,它的产生,主要有以下两个原因:一是使得函数表达式(匿名函数)有更简洁的语法,二是它拥有词法作用域的this值,也 ...

随机推荐

  1. 谈谈C#中的 Dispose 和 Finalize

    主要有一下几点需要知道: 1. Dispose需要实现IDisposable接口. Finalize就是c#中的析构方法,方法名以~开始. 2. Dispose由开发人员代码调用,而Finalize由 ...

  2. vc++编程之在程序中加入网址链接

    在vc++对话框编程中,我们处于某种需要(介绍自己的软件或者自己的博客)可以在对话框上增加一个网址链接,用户只要一点击,就进入了相应的网页,我在此演示下如何完成. 1 打开编译器,我们新建一个基于对话 ...

  3. [shell基础]——整数比较;字符串比较;文件测试;逻辑测试符

    整数比较方法一:[  ] 或 [[  ]]   (1) 此方法需要使用整数比较运算符.[标注:equal 等于   greater 大于   less-then 小于] (2) 使用时一定要注意前后一 ...

  4. [无人值守安装操作系统]_FTP+TFTP+DHCP+Kickstart+PXE_中遇到的错误

    本篇记录的是实验  http://www.cnblogs.com/snsdzjlz320/p/5629127.html  过程出现的问题 问题一: PXE-E11:ARP timeout TFTP c ...

  5. Objective-C面向对象(三)

    1.类的继承 OC的继承是单继承,每个子类只有一个直接父类 1.1 继承的特点 OC继承的语法 @interface SubClass :SuperClass { //成员变量定义 } //方法定义部 ...

  6. c判断括弧是否匹配

    这里我没有用堆栈.直接用一个数组input[SIZE]接收用户的输入,在遍历数组,对数组进行操作.已经匹配好的括弧直接用#号覆盖,最后遍历数组.如果数组只有#号,没有其他元素,则匹配.否则不匹配. / ...

  7. ios应运程序的五种状态

    ios应运程序的五种状态即转化  从apple的官方文档扣下来的 5状态: Not running The app has not been launched or was running but w ...

  8. c语言学习strcopy

    自己写了一个字符串复制函数: #include<stdio.h> #include<assert.h> char *mystrcpy(char *des,char *ser) ...

  9. Careercup - Microsoft面试题 - 6282862240202752

    2014-05-11 03:56 题目链接 原题: Given an integer array. Perform circular right shift by n. Give the best s ...

  10. mongoDB 入门指南、示例

    一.准备工作 1. 下载mongoDB 下载地址:http://www.mongodb.org/downloads 选择合适你的版本 相关文档:http://www.mongodb.org/displ ...