zoukankan      html  css  js  c++  java
  • 使用多窗体时, 关于节约内存和加快启动速度的思考与尝试


    刚接触 Delphi 时, 曾以为 Pi 是个常量; 当我知道它是个函数时, 曾被触动.

    但 Pi 返回的不过是占 10 个字节的 Extended 类型, 后来竟发现一些对象也这样干, 譬如:
    function Clipboard: TClipboard;                       { Clipbrd }
    function Printer: TPrinter;                           { Printers }
    function Languages: TLanguages;                       { SysUtils }
    function RootSprigList: TRootSprigList;               { TreeIntf }
    function ComClassManager: TComClassManager;           { ComObj }
    function ThemeServices: TThemeServices;               { Themes }
    function PropertyCategoryList: TPropertyCategoryList; { PropertyCategories }
    
    //这或许已是 Delphi 2010 中类似对象的全部.
    

    特别是其中常用的 Clipboard、Printer、Languages, 无需建立就可以像对象一样使用它们.

    这是怎么实现的呢? 查看源码, 它们都有类似的结构脉络(以 Clipboard 对象为例):
    interface
    ...
    function Clipboard: TClipboard; { 这一般在 interface 的尾部 }
    ...
    implementation
    ...
    var
      FClipboard: TClipboard; { 句柄变量 }
    ...
    function Clipboard: TClipboard; { 函数实现 }
    begin
      if FClipboard = nil then
        FClipboard := TClipboard.Create;
      Result := FClipboard;
    end;
    ...
    

    当然它们还有个释放的问题, 但我觉得这些对象往往也是它所在的单元也在频繁地使用, 一般到最后释放.

    它们巧妙的是:
    在我们不使用时, 它们并没有建立;
    有使用时立即建立;
    再使用时也不会重复建立.

    真是非常简单而巧妙的思路!

    不能不提另外两个相似的对象:
    Application {Forms.TApplication}
    Screen      {Forms.TScreen}
    
    //这个两个更常用, 但它们不是函数, 的确是对象变量; 它们是在程序生存期一直存在的.
    

    当一个工程有多个窗体时(恐怕单窗体的程序很少), 那些副窗体能否动态建立呢? 这样做肯定会节约资源、加快启动速度, 我想这无须测试.

    上面的做法给了我们很好的借鉴, 不过我想到了两种方法: 1、动态建立; 2、像上面一样通过函数建立.

    不管用哪种方法, 都先要禁止程序默认的对窗体的自动建立, 修改如图:



    当然也可以直接从工程文件中删除相关的建立代码.

    //此时, 我们已经不能使用如下代码打开窗体了:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2.Show; { 这会出错, 因为此时 Form2 还没有建立 }
    end;
    
    //可以这样:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2 := TForm2.Create(Application);
      Form2.Show;
    end;
    
    //但如果想上面这样, Form2 之后又驻留内存了, 怎么释放它呢? 难道要和 Application 一起释放吗?
    //我们的目的是节约内存, 能不能这样?:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2 := TForm2.Create(Application);
      Form2.Show; { 但 Form2.ShowModal; 可以 }
      Form2.Free;
    end;
    
    //尝试上面的代码, 窗体刚刚显示就被释放了, 此路不通!
    //还是有解决方案的, 在 Form2 的 OnClose 事件中写点代码:
    procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := caFree;
      { caNone, caHide, caFree, caMinimize 分别是: 取消、隐藏、释放、最小化 }
    end;
    
    //这样 Form1 中的调用代码也可以简化如下:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      with TForm2.Create(nil) do Show;
    end;
    

    这是第一方案, 第二方案准备学习使用前面提到的 Clipboard 等设计手法.

    模仿 Clipboard 的实现, 我们可以把 Form2 所在的 Unit2 单元改写为:
    unit Unit2;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;
    
    type
      TForm2 = class(TForm)
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      end;
    
    function Form2: TForm2;
    
    implementation
    
    {$R *.dfm}
    
    var
      FForm: TForm2;
    
    function Form2: TForm2;
    begin
      if FForm = nil then
        FForm := TForm2.Create(Application);
      Result := FForm;
    end;
    
    procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := caFree;
    end;
    
    end.
    

    这里的窗体和 Clipboard 还是有区别, 因为我们在用完以后要马上释放, 所以代码可以简化为:
    unit Unit2;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;
    
    type
      TForm2 = class(TForm)
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      end;
    
    function Form2: TForm2;
    
    implementation
    
    {$R *.dfm}
    
    function Form2: TForm2;
    begin
      Result := TForm2.Create(nil);
    end;
    
    procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := caFree;
    end;
    
    end.
    

    这样我们在主窗体又可以这样调用了:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2.Show;
    end;
    
    { 这和 Delphi 默认的区别可大了, 这里的 Form2 是个函数; 动态建立、动态释放 }
    

  • 相关阅读:
    Dao层
    I/O流
    导入第三方jar包
    怎么使用log4j
    JDBC访问数据库的步骤
    抽象和封装
    JDBC中PreparedStatement接口提供的execute、executeQuery和executeUpdate之间的区别及用法
    ResultSet next方法
    实体类(entity)的作用
    接口的作用
  • 原文地址:https://www.cnblogs.com/del/p/1651483.html
Copyright © 2011-2022 走看看