我对释放Interfaced对象表示怀疑。
我的界面如下所示:
type IBase = interface(IInterface)
function test: Boolean;
end;
这个其他类在所有接口类中都有一些我想要的属性:
type TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; virtual; abstract;
destructor Destroy;
end;
我得到了一些继承自前一个类的clases,并且还实现了接口。
type TExample= class(TBase, IBase)
public
function test: Boolean;
destructor Destroy;
end;
所以,使用这个模式,我可以使用这样的类:
procedure someProcedure(aux : IBase);
begin
aux.test; //Aux is an instance of TExample and I'm using the interfaced method
end;
我的问题是,我如何销毁这个作为IBase interfacedObject的辅助对象? 我试过这个东西,先检查一下这不是一个零对象:
(aux as TObject).Destroy; //Invalid Pointer operation
(aux as TInterfacedObject).Destroy; //Invalid Pointer operation
(aux as TExample).Destroy; Also invalid Pointer operation!!??
我没有释放对象,而是读取它,因为继承自TInterfacedObject并实现了接口,我应该使用它:
aux := nil;
参考计数器将完成魔术,但使用ReportMemoryLeaksOnShutdown:= True;在我的项目中有一些泄漏,代码永远不会到达析构函数的断点。
我错过了什么吗?
修改:
我改变了我的构造函数,现在就像:
type TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create;
destructor Destroy; override;
end;
type TExample= class(TBase, IBase)
public
function test: Boolean;
end;
现在我想这更有意义,因为TBase正在分配和释放对象,而TExample类继承了析构函数。
答案 0 :(得分:4)
不要手动释放界面对象!
当一个类派生自TInterfacedObject
时,它会自动引用计数,并且只要没有接口引用就会自动释放。
这是什么意思?
以下过程通过其接口TExample
引用IBase
的实例。当从堆栈中删除Obj
变量时,它会自动清除内存。
procedure Foo;
var
Obj: IBase;
begin
Obj := TExample.Create; // reference count will be set to 1
Obj.test;
end; // reference count will be set to 0 and Obj will be freed
下一个过程在其类名上引用TExample
的实例。此处的引用计数无效。编译器不包括对_AddRef
和_Release
的调用。所以这个程序会泄漏内存:
procedure Foo;
var
Obj: TExample;
begin
Obj := TExample.Create;
Obj.test;
end; // Obj will not be freed automatically
所以你需要自己清理堆:
procedure Foo;
var
Obj: TExample;
begin
Obj := TExample.Create;
try
Obj.test;
finally
Obj.Free;
end;
end;
当Obj
传递时,它可以危险。只要对象的引用存储在接口引用中。让我们看看:
procedure Bar(Obj: IBase);
begin
//...
end;
procedure Foo;
var
Obj: TExample;
begin
Obj := TExample.Create;
try
Bar(Obj);
Obj.test; // Access violation!
finally
Obj.Free;
end;
end;
这里会发生什么?
创建 Obj
并将其存储为类引用。引用计数为0.调用Bar(Obj)
时,对象将存储在接口引用中。编译器包括在调用_AddRef
时调用_Release
和Bar
。参考计数器将增加和减少,因此它再次变为0并且对象会自行销毁。
如何处理?
TInterfacedObject
的派生。 答案 1 :(得分:1)
你的例子有些奇怪。
由于您已将TBase
构造函数声明为virtual abstract
,因此必须在TExample
类中声明构造函数。
正如其他人所说,你必须将override
指令添加到析构函数中。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
FastMM4,
System.SysUtils, ADODB, ActiveX;
type
IBase = interface(IInterface)
function test: Boolean;
end;
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; virtual; abstract;
destructor Destroy; override;
end;
TExample= class(TBase, IBase)
public
function test: Boolean;
constructor Create; reintroduce; virtual;
destructor Destroy; override;
end;
{ TBase }
destructor TBase.Destroy;
begin
FQuery.Free;
FADOConnection.Free;
inherited;
end;
{ TExample }
constructor TExample.Create;
begin
//inherited;
FADOConnection := TADOConnection.Create(nil);
FQuery := TADOQuery.Create(nil);
end;
destructor TExample.Destroy;
begin
inherited;
end;
function TExample.test: Boolean;
begin
Result := False;
end;
var
example: IBase;
begin
CoInitialize(nil);
example := TExample.Create;
try
WriteLn(example.test);
finally
example := nil;
end;
CoUninitialize;
end.
另一件看起来很奇怪的事情是释放基础析构函数中的FADO
对象,因为它们是在派生类中创建的。
作为旁注,我更喜欢这样的设计:
type
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create;
destructor Destroy; override;
end;
TExample= class(TBase, IBase)
public
function test: Boolean;
constructor Create;
destructor Destroy; override;
end;
{ TBase }
constructor TBase.Create;
begin
inherited;
FADOConnection := TADOConnection.Create(nil);
FQuery := TADOQuery.Create(nil);
end;
destructor TBase.Destroy;
begin
FQuery.Free;
FADOConnection.Free;
inherited;
end;
{ TExample }
constructor TExample.Create;
begin
inherited;
. . .
end;
答案 2 :(得分:-2)
这里有一些问题:
首先,您需要将TBase
添加到析构函数中,如注释中所指出的那样。但是constructor
的声明真的是杀死整个事情:
你有一个虚拟的空TInterfacedObject
,它隐藏了TObject中的那个。除此之外,当你有一个type TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; virtual; abstract;
destructor Destroy;
end;
时,你应该使用界面而不是实例。
所以
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; reintroduce; virtual;
destructor Destroy; override;
end;
应该是
reintroduce
注意 procedure TForm1.FormCreate(Sender: TObject);
var
aux: TExample;
begin
aux := TExample.Create;
aux.Free;
end;
。
有了这个,并且覆盖添加到析构函数中,您可以释放对象,因为您习惯于:
procedure TForm1.FormCreate(Sender: TObject);
var
aux: iBase;
begin
aux := TExample.Create;
//do stuff with aux
end;
无需施放。在这里你创造了一个你自己的意志,你必须自己解放
但正如我之前所说的,当你有一个TInterfacedObject时,你应该使用该接口,当它不再被引用时,该对象将释放它自己。
所以前面的例子应该是这样的:
type
IBase = interface(IInterface)
function test: Boolean;
end;
TBase = class(TInterfacedObject)
protected
FQuery: TADOQuery;
FADOConnection: TADOConnection;
public
constructor Create; reintroduce; virtual;
destructor Destroy; override;
end;
TExample = class(TBase, IBase)
public
function test: Boolean;
destructor Destroy; override;
end;
{ TBase }
constructor TBase.Create;
begin
inherited;
end;
destructor TBase.Destroy;
begin
inherited;
end;
{ TExample }
destructor TExample.Destroy;
begin
test;
inherited;
end;
function TExample.test: Boolean;
begin
ShowMessage('');
end;
我的完整测试代码如下所示:
procedure TForm1.FormCreate(Sender: TObject);
var
aux: iBase;
begin
aux := TExample.Create;
end;
然后打电话给它:
k = input("Please input something") # e.g. test
dict = {}
dict[k] = "This is my input"
print(dict) # prints {'test': 'This is my input'}