在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.
答案 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;