【问题标题】:How to check if an object is already destroyed or not?如何检查一个对象是否已经被销毁?
【发布时间】:2019-01-23 21:25:34
【问题描述】:
  1. 我创建了一个对象
  2. 我将它作为参数传递给了其他地方。在其他地方释放我无法控制的对象。
  3. 在编码结束时,我尝试检查对象是否有效,然后将其销毁。 Assigned() 方法返回true(似乎是因为存储对象引用地址的变量)。但是,引用的对象已经被销毁,我遇到了异常。

我的问题是,我如何检查一个对象是否已经被销毁? 除了 Assigned() 之外,还有什么可以检查该对象是否仍然存在?

program Project1;
uses System.SysUtils;

type TObj = class
  public
    Name: string;
end;

var AnObj, AnObj2 : TObj;
begin
  try
    try
      AnObj := TObj.Create;
      AnObj.Name := 'Testing';

      AnObj2 := AnObj;     // AnObj passed to other procedures as param
      FreeAndNil(AnObj2);  // somewhere else "Free" the object out of my control
      // as a result, AnObj is still assigned but the object is destroyed
    finally
      if Assigned(AnObj) then // AnObj is assigned, HOW COULD I IMPROVE HERE?
        FreeAndNil(AnObj);    // Exception: Invalid pointer operation
    end;
  except
    on E:Exception do
      writeln(E.Message);
  end;
  readln;
end.

【问题讨论】:

  • 感谢您提供此信息,是的,它是重复的。
  • 这很简单:你不能。释放的指针看起来与有效指针完全一样。必须检查是您的程序逻辑应该防止的事情。如果您对此有疑问,请阅读 Dalija Prasnikar 关于内存管理的书。
  • 存在某种重复,但公认的答案给出了糟糕的建议,正如前 Delphi 架构师 Danny Thorpe 在他的评论中所显示的那样。 FreeAndNil 不能代替适当的内存管理。 FreeAndNil 通常是一种代码味道。

标签: delphi


【解决方案1】:

如何检查一个对象是否已经被销毁?

简短的回答是你不能。没有机制允许您在手动内存管理下检查对象的有效性。

如果您想跟踪有效性以进行调试,您可以使用自定义内存管理器(如完全调试模式下的 FastMM),它可以跟踪所有引用并在您访问悬空指针时报告。


FreeAndNilAssigned 组合仅在您对对象具有单一引用时才有效。如果你有更多,它就会崩溃,因为FreeAndNil 可以nil 只有你调用它的引用。所有其他引用都将成为悬空指针,Assigned 无法检测到悬空指针。

简而言之,Assigned 仅在引用指向有效对象实例或包含 nil 值时有效。在手动内存管理下,如果您需要使用Assigned,您必须跟踪并确保您的引用始终包含有效值。

在保持对对象的单一引用时使用Assigned

如果您对对象有单一引用,则可以使用Assigned 进行延迟初始化模式,如果尚未创建对象实例,您将在其中创建对象实例。但是您必须首先确保对象引用包含nil 引用。

仅当对象引用是类字段、对象实例字段或全局变量时,它们才会自动初始化为nil

如果您不打算在该对象引用被释放后重用它,您可以使用Free 来释放其内存。

如果你有可重用的引用,你想多次创建和释放对象实例,你必须使用FreeAndNil,或者在你调用Free之后将引用设置为nil。这是您确保Assigned 在对象被释放后工作的唯一方法。同样,这个Assigned/FreeAndNil 模式只有在你只保留一个对该对象的引用时才有效。

在保持对一个对象的多个引用时使用Assigned

如果可能的话,对一个对象实例的引用永远不要超过一个。如果您确实必须保留对对象实例的多个引用,则需要实现一些机制,以确保您可以通知所有引用该对象不再有效。

有几种方法可以做到这一点:

  1. 使用接口 - 它们的自动内存管理将防止悬空指针
  2. 使用TComponent 作为基类 - 当对象实例即将被销毁时,您可以使用它的通知系统来获取通知
  3. 实现您自己的通知系统来管理对对象实例的所有引用

注意:对于 2. 和 3. 您必须手动 nil 在收到通知时对对象的所有引用,它将被销毁。如果您不这样做,您未设置为nil 的任何引用都将无效,任何使用Assigned 的悬空引用都将无法正常工作。


如果您将对象作为参数传递,并且在该对象上调用了一些超出您控制的代码Free,那么您正在处理所有权转移。您创建了对象,但随后将所有权(释放对象的责任)转移给了其他代码。之后,您不应再次使用该对象引用(除非在某些狭窄的场景中,当您确定传输完成后立即执行代码不会触发对象释放时)。

只有当有一些通知机制(如前所述)可以在对象实例被释放时通知您时,您才能使用不属于您的对象引用(在您转移所有权之后)。

此外,您永远不应该释放您不拥有的对象。在您的示例中,即使您可以得到通知对象将被销毁,您所能做的只是nil 指向它的引用。你不应该尝试释放它。

【讨论】:

  • 感谢您的回答,这比之前发布的另一个要好得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
  • 2017-06-29
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多