如何通过避免_Release被调用来混合接口和类?

时间:2011-09-06 08:18:57

标签: delphi interface

在Delphi中使用Interfaces并覆盖引用计数时,可以绕过Delphi在接口达到零引用计数时发出的释放调用。

但是 - 当混合类和接口(非常有用)时,无论如何都始终调用_Release方法。问题是在下面的示例代码中,本地对象是nill-ed,但仍然会调用_Release - 除了无效内存。根据应用程序中的内存操作,在nilled localObject的旧位置调用_Release时可能会导致异常,如果未重用内存则无异常。

那么,编译器是否可以生成对_Release的调用“删除/阻止/避免/杀死/重定向/ vmt劫持/终止/打击/等等”?如果可以实现这一点,那么在Delphi中就有了适当的纯接口。

unit TestInterfaces;

interface

uses
  Classes,
  SysUtils;

type

  ITestInterface = interface
    ['{92D4D6E4-A67F-4DB4-96A9-9E1C40825F9C}']
    procedure Run;
  end;

  TTestClass = class(TInterfacedObject, ITestInterface)
  protected
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    procedure Run;
  end;

  TRunTestClass = class(TObject)
  protected
    FlocalInterface : ITestInterface;
    FlocalObject : TTestClass;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Test;
  end;

  procedure RunTest;
  procedure RunTestOnClass;

var
  globalInterface : ITestInterface;

implementation


procedure RunTest;
var
  localInterface : ITestInterface;
  localObject : TTestClass;
begin

  try

    //create an object
    localObject := TTestClass.Create;

    //local scope
    // causes _Release call when object is nilled
    localInterface := localObject;
    localInterface.Run;

    //or global scope
    // causes _Release call when exe shuts down - possibly on invalid memory location
    globalInterface := localObject;
    globalInterface.Run;

  finally
    //localInterface := nil; //--> forces _Release to be called
    FreeAndNil( localObject );
  end;

end;

procedure RunTestOnClass;
var
  FRunTestClass : TRunTestClass;
begin
  FRunTestClass := TRunTestClass.Create;
  FRunTestClass.Test;
  FRunTestClass.Free;
end;


{ TTheClass }

procedure TTestClass.Run;
begin
  beep;
end;

function TTestClass._AddRef: Integer;
begin
  result := -1;
end;

function TTestClass._Release: integer;
begin
  result := -1;
end;

{ TRunTestClass }

constructor TRunTestClass.Create;
begin
  FlocalObject := TTestClass.Create;
  FlocalInterface := FlocalObject;
end;

destructor TRunTestClass.Destroy;
begin
  //..
  FlocalObject.Free;
  //FlocalObject := nil;
  inherited;
end;

procedure TRunTestClass.Test;
begin
  FlocalInterface.Run;
end;

end.

2 个答案:

答案 0 :(得分:3)

没有切实可行的方法来实现您的目标。编译器将发出对_Release的调用,为了重击,你需要找到所有的调用站点。这不切实际。

我担心禁用引用计数生命周期管理时唯一可行的方法是确保在调用nil之前最终确定(即设置为Free)所有接口引用。

答案 1 :(得分:1)

使用界面时,您不再需要释放对象。当没有对同一对象的任何引用时,将自动释放interfaced对象。

在您的示例中,您必须删除它们在TInterfacedObject类中定义的TTestClass中的_Release和_Addref函数。

在RunTest过程中,您不需要仅将最终部分中的localObject设置为将globalInterface设置为nil。在程序结束后,localInterface将自动销毁本地对象。

try
  ... use your code
  ...
finnaly
  globalInnterface := nil;
end;

关于TTestRun.Destroy只是将这个析构函数留空了。你不能释放FlocalObject。

TTestRun.Destroy;
begin
  inherited;
end;
相关问题