如何检查物体是否已经被破坏?

时间:2019-01-23 21:25:35

标签: 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.

1 个答案:

答案 0 :(得分:4)

  

如何检查对象是否已被破坏?

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

如果要出于调试目的跟踪有效性,则可以使用自定义内存管理器(如处于完全调试模式的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指向该对象的引用。您不应该尝试释放它。