zoukankan      html  css  js  c++  java
  • [转]接口小论

      完成一个功能时用到观察者模式,将业务类的变化通知给外部的窗体,同时也通知给另一个类,假设为TTest,这个类继承自TInterfaceObject。窗体和TTest都实现了一个接口,因此业务类通过接口可以将变化通知给外部。 

      感觉这个类层次应用得很好,但问题出现了,业务类用IInterfaceList管理这些接口,程序关闭时,IInterfaceList将所有的接口置为Nil,按以前的理解,实现接口的类对象会同时被消毁。即TTest对象和窗体类对象自动会被消毁。但事实上并不是这样。

      经过一番研究,才明白个中原因,有结论如下:

    1.类如果从TInterfaceObject继承下来,并且实现某个接口。

    该接口变量可以自动释放实现该接口的类,例子如下:

    type

    ITest = interface(IInterface)
      procedure test;
    end;

    TTest = class(TInterfacedObject, ITest)
    public
      procedure test;
      destructor Destroy; override;
    end;

    {TTest}
    procedure TTest.test;
    begin
      showmessage('ok');
    end;

    destructor Destroy; 
    begin
      showmessage('ok');
      inherited;
    end;

    //测试
    var
      Test: ITest;
    begin
      Test := TTest.Create;
      try
        Test.test;
      finally
        Test := nil;
      end;
    end;

    执行后,弹出两次对话框,说明TTest对象最后被释放了,原因是Test := nil 后,最终于会调用TInterfaceObject的_Release方法,在里面如果引用计数为0,则调用Destroy释放自己。

    2.  类如果从TComponent继承下来,并且实现某个接口。 

    该接口变量不能自动释放实现该接口的类,把上面的TTest改为从TComponent继承,测试代码如下:

    var
      Test: ITest;
    begin
      Test := TTest.Create(nil);
      try
        Test.test;
      finally
        Test := nil;
      end;
    end;

     

      这时只弹出一个对话框,说明对象没有被释放,原因是Test := nil后,最终于会调用TComponent的_Release,而里面如果引用计数为0并不会释放自己。 

    3.要对象可以由接口自动释放,必须该类自己实现IInterface,并在_Release调用Destroy。例子如下:

    type
      ITest = interface(IInterface)
        procedure test;
      end;

      TTest = class(TComponent,  IInterface, ITest)
      public
        { IInterface }
        function QueryInterface(const IID: TGUID;
          out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
        { ITest }
        procedure test;
        destructor Destroy; override;
      end;
    ...
    { TTest }

    destructor TTest.Destroy;
    begin
      ShowMessage('ok');
      inherited;
    end;

    function TTest.QueryInterface(const IID: TGUID;
      out Obj): HResult;
    begin
      if GetInterface(IID, Obj) then
        Result := 0
      else
        Result := E_NOINTERFACE;
    end;

    procedure TTest.test;
    begin
      ShowMessage('ok');
    end;

    function TTest._AddRef: Integer;
    begin
      Result := InterlockedIncrement(FRefCount);
    end;

    function TTest._Release: Integer;
    begin
      Result := InterlockedDecrement(FRefCount);
      if Result = 0 then
        Destroy;
    end;

    //测试代码
    var
      Test: ITest;
    begin
      Test := TTest.Create;
      try
        Test.test;
      finally
        Test := nil;
      end;
    end;

    运行结果,对话框弹了两次,对象可以由接口释放了。

    Delphi的接口真是太不直观了。

  • 相关阅读:
    利用线程制作简单定时器
    VScode小白简介
    WebService介绍及C/C++访问
    Redis的C++与JavaScript访问操作
    Redis的安装配置及简单集群部署
    解决npm被墙的问题
    【学习整理】第三章 使用字符串
    【学习整理】第四章 字典
    【学习整理】第二章 列表和元祖
    【学习整理】第一章 基础知识部分 常用函数 简单语法
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/9208303.html
Copyright © 2011-2022 走看看