在我的代码中,我使用了一个小型数据存储类,它在不同的地方创建。为了避免内存泄漏并简化操作,我想使用引用计数,所以我做了
type TFileInfo = class (TInterfacedObject, IInterface)
并删除了我对TFileInfo.Free的所有手动调用。不幸的是Delphi报告了很多内存泄漏。搜索SO我发现以下问题解释了为什么这不起作用:
Why aren't descendants of TInterfacedObject garbage collected?
有一个解决方法,但它需要我(至少如果我做对了)编写一个自定义接口IFileInfo并为它提供了许多getter和setter,我想避免。
编辑我应该补充一点,我将创建的FileInfo对象插入到两种不同类型的哈希表中:一种来自TBucketList,另一种是来自Codegear论坛的哈希映射实现。在内部它们都是用户指针,因此情况就像在另一个问题中一样。
还有其他可能使Delphi中的对象使用引用计数吗?
答案 0 :(得分:8)
Delphi中的引用计数仅在您通过接口仅引用实例时才有效。只要混合接口引用和类引用,就会遇到麻烦。
基本上,您需要引用计数,而无需使用其中定义的所有方法和属性创建接口。有三种方法可以做到这一点,这些方法大致按我推荐的顺序排列。
Barry Kelly写了一篇关于Smart Pointers的帖子。它使用Delphi 2009中的Generics,但我很确定你可以将它硬编码到你正在使用的特定版本的类型,如果你还没有使用2009(它是一个很棒的版本BTW)。
使用更多版本的Delphi和更少修改的另一种方法是Janez Atmapuri Makovsek的value type wrapper。这是为TStringList实现的示例,但您可以针对任何类型进行调整。
第三种方法是创建一个接口指针(类似于Barry的智能指针,但不是那么聪明)。我相信JCL中有一个,但我不记得确切的细节。基本上这是一个在构造时接受TObject引用的接口。然后,当它的引用计数达到零时,它会在您传递给它的对象上调用free。此方法实际上仅适用于未作为参数传递的短期实例,因为您将引用计数引用与实际使用的引用分开。我建议使用其他两种方法中的一种,但如果您更喜欢这种方法并想了解更多信息,请告诉我。
这就是德尔福的事情,有一种自由的方式来完成事情。在我看来,选项#1是最好的 - 如果可以的话,获取Delphi 2009并使用该方法。
祝你好运!答案 1 :(得分:5)
不幸的是,只有在使用接口(在您的情况下是自定义接口IFileInfo)时,Delphi编译器才会为inc / dec引用计数生成必要的代码。此外,如果将接口强制转换为指针(或者TObject),则无法再进行引用计数。例如,假设全局变量列表:TList:
var ifi : IFileInfo;
begin
ifi := TFileInfo.Create;
list.Add(TFileInfo(ifi));
end;
方法返回后,list [list.Count - 1]将包含悬空指针。
因此,接口不能用于将它们转换为指针的hashmap中,hashmap实现必须将它们保留为IInterface。
答案 2 :(得分:3)
此功能是为接口提供的,但不是为对象提供的。
您可以创建类似的东西,但是您需要覆盖TObject的一些结构:
TRefCountObject = class (TObject)
private
FRefCount : Integer;
public
constructor Create;
procedure Free; reintroduce;
function RefCountedCopy: TRefCountObject;
end;
constructor TRefCountObject.Create;
begin
inherited;
FRefCount := 1;
end;
procedure TRefCountObject.Free;
begin
if self=nil then Exit;
Dec(FRefCount);
if FRefCount<=0 then
Destroy;
end;
function TRefCountObject.RefCountedCopy: TRefCountObject;
begin
Inc(FRefCount);
Result := self;
end;
您需要RefCountedCopy将对象分配给另一个变量。但是你有一个refcounted对象。
如何使用:
var1 := TRefCountObject.Create; // rc = 1
var2 := var1.RefCountedCopy; // rc = 2
var3 := var1.RefCountedCopy; // rc = 3
var2.Free; // rc = 2
var1.Free; // rc = 1
var4 := var3.RefCountedCopy; // rc = 2
var3.Free; // rc = 1
var4.Free; // rc = 0
答案 3 :(得分:3)
不要混合对象引用和接口引用。
var
Intf: IInterface;
Obj: TFileInfo;
begin
// Interface Reference
Intf := TFileInfo.Create; // Intf is freed by reference counting,
// because it's an interface reference
// Object Reference
Obj := TFileInfo.Create;
Obj.Free; // Free is necessary
// Dangerous: Mixing
Obj := TFileInfo.Create;
Intf := Obj; // Intf takes over ownership and destroys Obj when nil!
Intf := nil; // reference is destroyed here, and Obj now points to garbage
Obj.Free; // this will crash (AV) as Obj is not nil, but the underlying object
// is already destroyed
end;
答案 4 :(得分:3)
如果你想要消除对TObject实例的免费调用,那么你可能想要查看本机Delphi的垃圾收集器。我知道2种不同的垃圾收集器和垃圾收集技术,每种都有利弊。
Native Precise Tracking Garbage Collector作者:HenrickHellström
Garbage Collector for Delphi Objects and Components(不是真正的垃圾收集器,更像是一个记忆包。)
其中一个可能适合你。
答案 5 :(得分:1)
要添加已经说过的内容,如果要存储对Interfaces的引用,而不是使用TList,请使用 TInterfaceList 。引用计数将始终如一。
答案 6 :(得分:0)
对此有一个很长的解释,但简而言之:继承TInterfacedObject(而不是自己调用Free)是不够的,你需要使用object-factory-dynamic为你创建对象,并使用interface-到处都是对象的指针,而不仅仅是对象引用变量。 (是的,这意味着你不能只是在不查看的情况下切换'旧代码')