【问题标题】:Interfaces without reference counting没有引用计数的接口
【发布时间】:2012-10-30 08:39:24
【问题描述】:

在阅读了 StackOverflow 上关于对接口使用自动引用计数的缺点的许多帖子后,我开始尝试手动对每个接口实例化进行引用计数。

尝试了一个下午我放弃了!

为什么我在调用 FreeAndNil(p) 时会出现访问冲突?

以下是我的简单单元的完整列表。

unit fMainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm4 = class(TForm)
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  end;

type
  IPersona = interface(IInterface)
  ['{44483AA7-2A22-41E6-BA98-F3380184ACD7}']
    function GetNome: string;
    procedure SetNome(const Value: string);
    property Nome: string read GetNome write SetNome;
  end;

type
  TPersona = class(TObject, IPersona)
  strict private
    FNome: string;
    function GetNome: string;
    procedure SetNome(const Value: string);
  protected
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  public
    constructor Create(const ANome: string);
    destructor Destroy; override;
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

procedure TForm4.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := True;
end;

procedure TForm4.btn1Click(Sender: TObject);
var
  p: IPersona;
begin
  p := TPersona.Create('Fabio');
  try
    ShowMessage(p.Nome);
  finally
    FreeAndNil(p);
  end;
end;

constructor TPersona.Create(const ANome: string);
begin
  inherited Create;
  FNome := ANome;
end;

destructor TPersona.Destroy;
begin
  inherited Destroy;
end;

function TPersona._AddRef: Integer;
begin
  Result := -1
end;

function TPersona._Release: Integer;
begin
  Result := -1
end;

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

function TPersona.GetNome: string;
begin
  Result := FNome;
end;

procedure TPersona.SetNome(const Value: string);
begin
  FNome := Value;
end;

end.

【问题讨论】:

    标签: delphi


    【解决方案1】:

    发生访问冲突是因为FreeAndNil 接收到一个预期为对象引用的无类型var 参数。您正在传递一个不符合要求的接口引用。不幸的是,您只能在运行时发现。在我看来,这是反对使用FreeAndNil 的最强点。

    您的引用计数会禁用接口引用计数机制的生命周期管理。为了销毁一个对象,你需要调用它的析构函数。为了做到这一点,您必须有权访问析构函数。您的界面不公开析构函数(也不应该公开)。所以,我们可以推断,为了销毁对象,你需要有一个对象引用。

    这里有一些选项:

    var
      obj: TPersona;
      intf: IPersona;
    ....
    obj := TPersona.Create('Fabio');
    try
      intf := obj;
      //do stuff with intf
    finally
      obj.Free;
      // or FreeAndNil(obj) if you prefer
    end;
    

    或者你可以这样做

    var
      intf: IPersona;
    ....
    intf := TPersona.Create('Fabio');
    try
      //do stuff with intf
    finally
      (intf as TObject).Free;
    end;
    

    【讨论】:

    • +1 我不知道您可以简单地将接口类型转换为 TObject。 (也没有测试过,但来自你,我认为它是正确的)
    • @Lieven 仅在一些较新的 Delphi 版本中支持。 2009 年或 2010 年。不记得确切的时间。
    • 大卫谢谢你!您的解决方案(两者)都像魅力一样工作(至于您在 StackOverflow 上发布的每个答案 ;-))太棒了!!!!
    • 出于好奇:我是否总是需要在接口声明中放置 GUID?我试图在我的代码中删除 GUID,一切似乎都正常。
    • 我总是把它们放进去,因为没有它们你就不能用 as 操作符、Supports 等将一个接口转换为另一个接口。
    【解决方案2】:

    您不能将FreeAndNil() 与接口引用一起使用,只能使用对象引用。如果您启用了接口的引用计数,您只需将nil 分配给接口引用(或让它超出范围)以正确释放对象,例如:

    type
      TPersona = class(TInterfacedObject, IPersona)
      strict private
        FNome: string;
        function GetNome: string;
        procedure SetNome(const Value: string);
      public
        constructor Create(const ANome: string);
        destructor Destroy; override;
      end;
    
    procedure TForm4.btn1Click(Sender: TObject);
    var
      p: IPersona;
    begin
      p := TPersona.Create('Fabio');
      try
        ShowMessage(p.Nome);
      finally
        p := nil;
      end;
    end;
    

    但是由于你已经禁用了接口上的引用计数,你需要在你的代码中重新使用普通的对象引用变量,例如:

    procedure TForm4.btn1Click(Sender: TObject);
    var
      p: TPersona;
      intf: IPersona;
    begin
      p := TPersona.Create('Fabio');
      try
        if Supports(p, IPersona, intf) then
          ShowMessage(intf.Nome);
      finally
        FreeAndNil(p);
      end;
    end;
    

    【讨论】:

      猜你喜欢
      • 2014-05-04
      • 2010-09-15
      • 1970-01-01
      • 1970-01-01
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      • 2013-08-11
      • 2021-08-17
      相关资源
      最近更新 更多