zoukankan      html  css  js  c++  java
  • MakeObjectInstance的简单理解

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

  • 相关阅读:
    Spring Cloud Gateway 使用
    Spring Cloud Stream 整合 RabbitMQ
    机器学习知识总结---1、回归和分类是可以相互转换的
    23个适合Java开发者的大数据工具和框架
    serilog Getting Started
    NLog.Targets.ElasticSearch
    RollingFileAppender bufferSize is not working? Here is an alternative
    go微服务示例(k8s istio grpc swagger postgres增量更新sql等功能)
    undefined: grpc.SupportPackageIsVersion6 和 undefined: grpc.ClientConnInterface 解决办法
    micrometer埋点(Spring boot 2.X metrics)
  • 原文地址:https://www.cnblogs.com/findumars/p/4111164.html
Copyright © 2011-2022 走看看