昨天把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. NewRelicAgent(CustomAnalyticEvent.cxx.o), building for iOS simulator, but linking in object file built for OSX, for architecture x8(botched)

    昨天遇到一个问题,在项目swift1.2适配swift2.0的过程中,修改完毕之后,运行报错如下: /Pods/NewRelicAgent/NewRelic_iOS_Agent_5.1.0/NewRe ...

  2. VC++ MFC 如何实现在编辑框中输出具有换行功能的文段 01

    很久不来写东西了,昨天睡觉前写个小工具,突然,这玩意不会换行怎么整... 首先是第一步,获取字符串的长度,转载自白乔的文章. ------------------------------------- ...

  3. VBS基础篇 - 常用函数

    Option Explicit '*********************************Date/Time函数******************************* 'CDate函 ...

  4. nodejs笔记二--文件I/O;

    一.写入文件: fs.writeFile(filename, data, callback),数据参数可以是string或者是Buffer,编码格式参数可选,默认为"utf8",回 ...

  5. Careercup - Google面试题 - 6332750214725632

    2014-05-06 10:18 题目链接 原题: Given a ,) (,) (,), (,) should be returned. Some suggest to use Interval T ...

  6. 【转载】oracle 分区表详解

    一.分区表的概述:     Oracle的表分区功能通过改善可管理性.性能和可用性,从而为各式应用程序带来了极大的好处.通常,分区可以使某些查询以及维护操作的性能大大提高.此外,分区还可以极大简化常见 ...

  7. 06.Hibernate实体类生命周期

        前言:Session接口是Hibernate向应用程序提供的操作数据库的主要接口,它提供了基本的增删查改方法,而且Session具有一个缓存它是Hibernate的一级缓存.站在持久化层的角度 ...

  8. 【Python】代码调试(pdb与logging使用)

    一.pdb使用 pdb 是 python 自带的一个包,为 python 程序提供了一种交互的源代码调试功能,主要特性包括设置断点.单步调试.进入函数调试.查看当前代码.查看栈片段.动态改变变量的值等 ...

  9. 【BZOJ】【1049】【HAOI2006】数字序列

    DP 第一问比较水……a[i]-=i 以后就变成最长不下降子序列问题了,第二问这个结论好神奇,考试的时候怎么破?大胆猜想,不用证明?TAT 题解:http://pan.baidu.com/share/ ...

  10. eclipse中设置中文javadoc+如何查看class的中文javadoc

    一.  eclipse中设置中文javadoc 1.先到http://download.java.net/jdk/jdk-api-localizations/jdk-api-zh-cn/publish ...