为了提供尽可能多的信息,这里是我正在做的事情的一个非常基本的例子
type
IMyInterface = interface
[THE_GUID_HERE]
// some methods
end;
TMyInterfaceArray = Array of IMyInterface;
TMyInterfacedObject = class(TInterfacedObject, IMyInterface)
// implementation of the Interface etc. here
end;
TContainingObject = class
private
FIObjectArray: TMyInterfaceArray;
public
constructor Create;
destructor Destroy; override;
procedure NewInstanceOfInterfacedObject;
end;
implementation
constructor TContainingObject.Create;
begin
inherited;
// Just to illustrate that an Instance is being created...
NewInstanceOfInterfacedObject;
end;
destructor TContainingObject.Destroy;
var
I: Integer;
begin
for I := Low(FIObjectArray) to High(FIObjectArray) do
FIObjectArray[I] := nil;
SetLength(FIObjectArray, 0); // Array collapsed
inherited;
end;
procedure TContainingObject.NewInstanceOfInterfacedObject;
var
LIndex: Integer;
begin
LIndex := Length(FIObjectArray);
SetLength(FIObjectArray, LIndex + 1);
FIObjectArray[LIndex] := TMyInterfacedObject.Create;
end;
好的,因此创建了TContainingObject
的实例,然后创建了TMyInterfacedObject
的实例,存储在IMyInterface
的数组中。
调用TContainingObject
的{{1}}时,它只是引用并折叠数组。
我遇到的问题是,在任何地方都没有其他引用,destructor
的{{1}}永远不会被调用,因此内存泄漏。
我做错了什么,或者Delphi的引用计数系统无法应对接口类型数组中接口对象的简单概念?
感谢您的任何建议!
更多信息
TMyInterfacedObject
提供了一个Array属性来访问Array中包含的destructor
个别实例。
在我的实际代码中,多个接口类型之间存在循环引用。
我们建议TContainingObject
包含函数IMyInterface
,IMyInterface
包含GetSomething: IAnotherInterface
(循环引用)。
这会导致我的问题吗?如果是这样,循环引用是绝对必需,那么解决方案的目的是什么呢?
答案 0 :(得分:5)
如果IMyInterface
的实施包含IAnotherInterface
成员,并且IAnotherInterface
的实施包含IMyInterface
成员,并且他们互相引用,那么他们的引用计数除非你清除其中一个引用,否则永远不会降到0,这可能意味着在接口中添加方法来执行此操作,例如:
type
IAnotherInterface = interface;
IMyInterface = interface
['{guid}']
function GetAnotherInterface: IAnotherInterface;
procedure SetAnotherInterface(Value: IAnotherInterface);
property AnotherInterface: IAnotherInterface read GetAnotherInterface write SetAnotherInterface;
end;
IAnotherInterface = interface
['{guid}']
function GetMyInterface: IMyInterface;
procedure SetMyInterface(Value: IMyInterface);
property MyInterface: IMyInterface read GetMyInterface write SetMyInterface;
end;
type
TMyInterface = class(TInterfacedObject, IMyInterface)
private
FAnotherInterface: IAnotherInterface;
public
function GetAnotherInterface: IAnotherInterface;
procedure SetAnotherInterface(Value: IAnotherInterface);
end;
TAnotherInterface = class(TInterfacedObject, IAnotherInterface)
private
FMyInterface: IMyInterface;
public
function GetMyInterface: IMyInterface;
procedure SetMyInterface(Value: IMyInterface);
end;
function TMyInterface.GetAnotherInterface;
begin
Result := FAnotherInterface;
end;
procedure TMyInterface.SetAnotherInterface(Value: IAnotherInterface);
begin
if FAnotherInterface <> Value then
begin
if FAnotherInterface <> nil then FAnotherInterface.SetMyInterface(nil);
FAnotherInterface := Value;
if FAnotherInterface <> nil then FAnotherInterface.SetMyInterface(Self);
end;
end;
function TAnotherInterface.GetMyInterface: IMyInterface;
begin
Result := FMyInterface;
end;
procedure TAnotherInterface.SetMyInterface(Value: IMyInterface);
begin
if FMyInterface <> Value then
begin
if FMyInterface <> nil then FMyInterface.SetAnotherInterface(nil);
FMyInterface := Value;
if FMyInterface <> nil then FMyInterface.SetAnotherInterface(Self);
end;
end;
现在,当您没有明确地释放其中一个引用时,请查看引用计数:
var
I: IMyInterface;
J: IAnotherInterface;
begin
I := TMyInterface.Create; // I.RefCnt becomes 1
J := TAnotherInterface.Create; // J.RefCnt becomes 1
I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2
...
{
// implicit when scope is cleared:
I := nil; // I.RefCnt becomes 1, I is NOT freed
J := nil; // J.RefCnt becomes 1, J is NOT freed
}
end;
现在为其中一个引用添加一个显式版本:
var
I: IMyInterface;
J: IAnotherInterface;
begin
I := TMyInterface.Create; // I.RefCnt becomes 1
J := TAnotherInterface.Create; // J.RefCnt becomes 1
I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2
...
I.AnotherInterface := nil; // I.RefCnt becomes 1, J.RefCnt becomes 1
{
// implicit when scope is cleared:
I := nil; // I.RefCnt becomes 0, I is freed
J := nil; // J.RefCnt becomes 0, J is freed
}
end;
var
I: IMyInterface;
J: IAnotherInterface;
begin
I := TMyInterface.Create; // I.RefCnt becomes 1
J := TAnotherInterface.Create; // J.RefCnt becomes 1
I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2
J := nil; // I.RefCnt still 2, J.RefCnt becomes 1, J is NOT freed yet
...
I.AnotherInterface := nil; // I.RefCnt becomes 1, J.RefCnt becomes 0, J is freed
{
// implicit when scope is cleared:
I := nil; // I.RefCnt becomes 0, I is freed
}
end;
答案 1 :(得分:0)
当我在Delphi XE2中运行你的代码示例时(修复了一个小错误 - 请参阅编辑),它运行正常,并按预期调用TMyInterfacedObject
析构函数。