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

Eri*_*eng 1 delphi

  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.
Run Code Online (Sandbox Code Playgroud)

Dal*_*kar 5

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

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

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


FreeAndNilAssigned仅在您对对象有单一引用时才有效。如果您有更多的它分崩离析,因为FreeAndNil可以nil只参考你叫它时。所有其他引用将成为悬空指针并且Assigned无法检测悬空指针。

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

使用Assigned时,你保持单身的对象引用

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

nil只有当对象是类字段、对象实例字段或全局变量时,对象引用才会被自动初始化。

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

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

使用Assigned时,你保持多个对象引用

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

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

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

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


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

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

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