【问题标题】:How to free an object which is in a record?如何释放记录中的对象?
【发布时间】:2012-10-12 14:31:39
【问题描述】:

我猜我遇到了一个棘手的情况。我需要能够释放作为记录字段的对象。如果它是一个类,我通常会在析构函数中编写清理代码。但是既然记录类型不能引入“析构函数”,那怎么可能调用TObject(Field).Free;

我预测会有两种用法:

  1. 用新记录替换记录。

    我认为这种用法很容易实现。由于记录是值类型,因此它们在赋值时被复制,我可以重载赋值运算符并释放旧记录所拥有的对象。

    编辑:无法重载赋值。这对我来说是一个新信息..)

  2. 退出定义记录变量的范围。

    我可以想到一个释放对象的私有方法,并且可以在范围激发上手动调用此方法。但是,这里有同样的问题:如何使它更记录?这种行为有点像上课......

这是一个示例(显然不是预期用途):

TProperties = record
  ... some other spesific typed fields: Integers, pointers etc..
  FBaseData: Pointer;

  FAdditionalData: TList<Pointer>;
  //FAdditionalData: array of Pointer; this was the first intended definition
end;

假设,

FAdditionalData:=TList<Pointer>.Crete;

在记录构造函数中调用或通过像公开访问字段一样在记录变量范围内手动调用

procedure TFormX.ButtonXClick(Sender: TObject);
var
  rec: TProperties;
begin
  //rec:=TProperties.Create(with some parameters);

  rec.FAdditionalData:=TList<Pointer>.Create;

  //do some work with rec
end;

退出 ButtonClick 范围后,rec 不再存在,但 TList 仍然存在,这会导致内存泄漏...

【问题讨论】:

  • 记录分配不能超载。
  • 我不知道这一点(以前从不需要),但我现在知道了:)是的,它无法超载...

标签: delphi record


【解决方案1】:

如果您在记录中只有一个对象引用,那么您无法让编译器帮助您。您全权负责该对象的生命周期。您不能重载赋值运算符,并且您不会收到任何范围确定的通知。

你可以做的是添加一个保护接口来管理对象的生命周期。

TMyRecord = record
  obj: TMyObject;
  guard: IInterface;
end;

您需要确保TMyObject 通过引用计数来管理其生命周期。例如从TInterfacedObject派生。

当你初始化记录时,你这样做:

rec.obj := TMyObject.Create;
rec.guard := rec.obj;

此时,记录的guard 字段现在将管理您对象的生命周期。

事实上,如果你想进一步推动这个想法,你可以构建一个专门的类来保护对象的生命周期。这样就不再限制您在课堂上实施IInterface。网上有很多例子可以说明这项技术。例如,我提供 Jarrod Hollingworth 的文章标题为 Smart Pointers,Barry Kelly 的文章标题为 Reference-counted pointers, revisited。还有更多。这是一个老把戏!

但是请注意,您在这里看到的是值类型和引用类型的奇怪混合。从表面上看,记录是值类型。然而,这个行为就像一个引用类型。如果您在记录中有其他字段是值类型,那么这将更加令人困惑。处理此类记录时,您需要非常注意此问题。

从表面上看,在不了解您的设计的情况下,我倾向于建议您不要将对象引用放入记录中。它们更适合内部引用类型,即类。

【讨论】:

  • 我建议先以最后一段开头。如果一个人对生命周期管理不是非常非常小心,那么将类放入记录中是一条通往错误代码的捷径。
  • 非常翔实的答案,谢谢赫弗南先生。我将尝试这些方法。我需要更新问题以使设计更加清晰。对象字段是 TList s,但似乎我会把它变回原来的样子:array of Pointer
  • 使用动态数组解决生命周期管理。您可以考虑使用 Synopse 中出色的 TDynArray 来更轻松地使用动态数组。
【解决方案2】:

我记得有人创建了一个名为 TLifetimeWatcher 的类。基本上,它看起来像:

TLifetimeWatcher = class(TInterfacedObject)
private
  fInstance: TObject;
  fProc: TProc; 
public
  constructor Create(instance: TObject); overload;
  constructor Create(instance: TObject; proc: TProc); overload;
  destructor Destroy; override;
end;

// (cleanup) proc 如果被赋值,将在析构函数中执行,否则将通过调用 Free 方法释放实例。

【讨论】:

猜你喜欢
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多