【问题标题】:Access violation when freeing TObjectList释放 TObjectList 时访问冲突
【发布时间】:2011-10-28 02:13:06
【问题描述】:

我有以下 Delphi 代码:

destructor TXX_XXXX.Destroy;
var
i: Integer;
begin
  if Assigned(Allocations) then
  begin
    for i:=0 to (Allocations.Count - 1) do 
    begin
      try
      TXX_ALOC(Allocations.Items[i]).Free;
      except on Ex:Exception do
      begin
        OutputDebugString(PChar('Exception Error Message '+ Ex.Message));
      end;
      end;
    end;

        // Above code works well - no exception

        try
    FreeAndNil(Allocations); {Exception Here}
    except on E:Exception do
    begin
      OutputDebugString(PChar('Exception in xxxxxxxxx.pas'+E.Message));
    end;
    end;
  end;
  inherited;
end;

模块“Vcl50.bpl”中地址 4003AB4 的访问冲突。读取地址 2980BFFC

我知道访问冲突通常是由

引起的
  1. 释放一些之前被释放的对象
  2. 使用一些没有初始化的对象

但在我做免费之前,我检查了分配已分配。如果我放弃异常处理,我的应用程序将抛出一些错误错误。 Allocations 是一个 TObjectList,如果它是一个数组 - 我会怀疑我没有为数组分配长度,但它是一个 TObjectList。

非常感谢!

【问题讨论】:

  • “但在我免费之前,我检查了分配是否已分配”...请注意,这仅在您不仅调用 Free 还明确设置对 nil 的引用时有帮助(或更好地使用FreeAndNil)。 Free 没有设置对nil 的引用,这就是Assigned 检查的内容!

标签: delphi free access-violation


【解决方案1】:

TObjectList 通常会负责销毁其内容。在这种情况下不要释放你的对象。这会在释放 TObjectList 时导致访问冲突,因为它会再次尝试释放包含的对象。

对象列表的这种行为可以在其构造函数中控制:

constructor TObjectList.Create(AOwnsObjects: Boolean);

使用这个来指定您是否希望列表拥有它的内容(意味着:当它从列表中删除或列表被销毁时,它负责销毁项目)或不。没有参数的构造函数(您可能使用过)将其设置为true

您可能只需要像TList 这样的列表,但用于存储对象。如果是这种情况,请像这样创建您的列表:

Allocations:= TObjectList.Create(False);

但是,如果您想要自动销毁行为,那么只需删除 for 循环。对象列表将销毁您的 TXX_ALOC 对象。

【讨论】:

    【解决方案2】:

    通常,在清除列表时,您希望从头到尾循环,即

    for i := (Allocations.Count - 1) downto 0 do begin
       Delete(Allocations.Items[i]);
    end
    

    但在TObjectList 的情况下,如果列表拥有对象(默认情况下会这样做),您不应该在销毁容器之前释放它们,因为列表会为您执行此操作。在上面的代码中,如果列表拥有对象,那么调用 Delete 也会释放对象。

    【讨论】:

    • 因为他没有删除它们,所以并不那么重要。但通常你会这样做。
    • 清空列表释放列表中的元素
    • 一般是的。但是,如果存在一些更复杂的所有权模型,那么销毁 obj 可能会导致列表从自身中删除 obj。并且向零循环也略微优化,所以恕我直言,无论如何使用它是个好主意。但是我现在将示例代码更改为使用Delete
    • 我还没有遇到更复杂的所有权模型,所以在这里很难理解——为什么要使用 Delete 而不是 Free。
    • @spspli 如果 TObjectList 拥有它所包含的对象,那么调用 Delete 将从列表中删除该项目并释放它。但是由于列表中对象的数量发生了变化,你必须循环到 0,否则你会得到错误,因为 for 循环索引只计算一次,在循环的开始。
    【解决方案3】:

    您有 2 个选项...

    1) 如果您希望 Delphi 在将对象从 ObjectList 中删除时自动释放这些对象,请创建将 aOwnsObjects 设置为 true 的 TObjectList。在您的 Destructor 中,简单的 FreeAndNil 是 ObjectList 本身。这将从列表中删除所有对象并自动释放它们。由于释放 ObjectList 将自动释放该列表中包含的对象,因此您的代码可能如下所示:

    destructor TXX_XXXX.Destroy;
    begin
      FreeAndNil( Allocations ); //
      inherited;
    end;
    

    2) 创建 TObjectList 并将 aOwnsObjects 设置为 False,在这种情况下,您必须自己处理释放列表中的对象。你的代码可能看起来像这样:

    destructor TXX_XXXX.Destroy;
    var
      i       : Integer;
      oObject : TObject;
    begin
      if Assigned(Allocations) then
      begin
        for i:= Pred( Allocations.Count ) downto 0 do
        begin
          // Get a reference to the Object
          oObject := TXX_ALOC(Allocations.Items[i]);
          // Remove the object from the object list
          Allocations.Delete( i );
          // Free the Object
          FreeAndNil( oObject );
        end;
      end;
    
      FreeAndNil( Allocations );
      inherited;
    end;
    

    【讨论】:

    • 循环中的FreeAndNil( oObject ); 是“矫枉过正”,因为您正在使用本地变量。只需oObject.Free 就可以了。我相信鲁迪很快就会教你:)
    • 使用FreeAndNil 即使是局部变量也不会伤害恕我直言,它的好处是人们不必每次都考虑使用哪一个。
    • 看这里FreeFreeAndNil的讨论:stackoverflow.com/questions/3159376/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-22
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    相关资源
    最近更新 更多