Delphi 对象的引用计数
在我的代码中,我使用了一个小的数据存储类,它是在不同的地方创建的。为了避免内存泄漏和简化事情,我想使用引用计数,所以我这样做了Delphi 对象的引用计数,delphi,interface,reference-counting,tinterfacedobject,Delphi,Interface,Reference Counting,Tinterfacedobject,在我的代码中,我使用了一个小的数据存储类,它是在不同的地方创建的。为了避免内存泄漏和简化事情,我想使用引用计数,所以我这样做了 type TFileInfo = class (TInterfacedObject, IInterface) 并删除了我对TFileInfo.Free的所有手动调用。不幸的是,Delphi报告了大量内存泄漏。继续搜索,我发现了以下问题,解释了为什么这不起作用: 这里介绍了一种解决方法,但它要求我(至少如果我做对了的话)编写一个自定义接口IFileInfo,并为它提供
type TFileInfo = class (TInterfacedObject, IInterface)
并删除了我对TFileInfo.Free的所有手动调用。不幸的是,Delphi报告了大量内存泄漏。继续搜索,我发现了以下问题,解释了为什么这不起作用:
这里介绍了一种解决方法,但它要求我(至少如果我做对了的话)编写一个自定义接口IFileInfo,并为它提供许多getter和setter,这是我想要避免的
编辑我应该补充一点,我将create FileInfo对象插入到两种不同类型的哈希表中:一种是从TBucketList下降的,另一种是来自Codegear论坛的哈希映射实现。在内部,它们都是用户指针,因此情况与另一个问题类似
是否有其他可能使Delphi中的对象使用引用计数?此功能用于接口,但不用于对象 您可以创建类似的内容,但需要覆盖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;
不幸的是,Delphi编译器仅在您使用接口(在您的示例中是自定义接口IFileInfo)时生成inc/dec引用计数所需的代码。此外,若接口被转换为指针(或TObject),那个么引用计数也是不可能的。例如,假设全局变量列表:TList:
var ifi : IFileInfo;
begin
ifi := TFileInfo.Create;
list.Add(TFileInfo(ifi));
end;
方法返回后,list[list.Count-1]将包含悬空指针
因此,接口不能用于将其强制转换为指针的hashmap中,hashmap实现必须将其保留为接口。对此有很长的解释,但简而言之:从TInterfacedObject继承(而不是自己调用Free)是不够的,您需要使用对象工厂动态为您创建对象,并在任何地方使用指向对象的接口指针,而不仅仅是对象引用变量。(是的,这意味着您不能在不查看的情况下切换“旧代码”)不要混合对象引用和接口引用
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;
Delphi中的引用计数仅在通过接口对实例进行引用时有效。一旦你混合了接口引用和类引用,你就有麻烦了 本质上,您希望引用计数,而不需要创建包含其中定义的所有方法和属性的接口。有三种方法可以做到这一点,它们大致按照我推荐的顺序排列
祝你好运 要添加到已经说过的内容中,如果要存储对接口的引用,而不是使用TList,请使用TInterfaceList。ref计数将持续工作。如果您想消除对TObject实例的释放调用,那么您可能需要查看本机Delphi的垃圾收集器。我知道有两种不同的垃圾收集器和一种垃圾收集技术,每种都有优点和缺点
- 亨里克·赫尔斯特罗姆
- 巴里·凯利
- Rossen Assenov(不是一个真正的垃圾收集器,更像是一个记忆袋。)
其中一个可能适合您。谢谢您的详细回答!但我并不完全理解。我还得打电话给TRefCountObject,有空吗?或者我如何使用它?所以我仍然必须确保每个对象至少调用一次Free,对吗?没有办法避免这个问题吗?这个问题没有灵丹妙药。正如我正确理解的,你的问题是你有一个对象被两个列表引用。通常,您声明其中一个为“所有者”,负责销毁和删除其他链接。但即使只有一个列表,也需要释放每个对象。或者使用带有垃圾收集的语言。好的,那么,我将返回到手动释放对象内存的版本。我只是好奇,是否有一种方法可以借助接口“模拟”普通对象的垃圾收集。正如你和其他海报所指出的,没有。谢谢你提供的宝贵信息!但是,如果我将所有对象插入到哈希映射中,编译器是否会生成正确的inc/dec?此代码不会编译。不可能将接口类型转换为对象。它可以编译,但不起作用,这就是我试图演示的要点。我再次编辑了答案,希望现在答案更清楚。很难选择接受的答案,但我发现你的答案最清楚地描述了实际问题(基于指针的数据结构)。我再次阅读了你的问题,我
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;