• [转]接口小论


      完成一个功能时用到观察者模式,将业务类的变化通知给外部的窗体,同时也通知给另一个类,假设为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的接口真是太不直观了。

  • 相关阅读:
    kafka 项目实战
    7.DHCP的相关命令
    3.centos 7执行service iptables save报错问题
    39.NFS(网络文件系统)
    37.Samba 文件共享服务1--配置共享资源
    36.Samba 文件共享服务1--安装及配置参数解释
    35.简单文件传输协议
    34.vsftpd服务程序--虚拟用户模式
    33.vsftpd服务程序--本地用户模式
    32.vsftpd服务程序--匿名开放模式
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/9208303.html
Copyright © 2020-2023  润新知