Delphi 释放TObjectList时发生访问冲突

Delphi 释放TObjectList时发生访问冲突,delphi,free,access-violation,Delphi,Free,Access Violation,我有以下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

我有以下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

我知道访问违规通常是由

  • 释放以前已释放的对象
  • 在不初始化的情况下使用某些对象

  • 但在这里,在我做免费之前,我检查了分配是否已分配。如果我放弃异常处理,我的应用程序将抛出一个“有问题”错误。 分配是一个TObjectList,如果它是一个数组-我怀疑我没有给数组分配长度,但它是一个TObjectList


    非常感谢

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

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

    constructor TObjectList.Create(AOwnsObjects: Boolean);
    
    使用此选项可以指定是否希望列表拥有其内容(意味着:当项目从列表中删除或列表被销毁时,它负责销毁该项目)。不带参数的构造函数(您可能使用过)将其设置为
    true

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

    Allocations:= TObjectList.Create(False);
    

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

    通常,在清除要从一端循环到另一端的列表时

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

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

    有两个选项供您选择

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

    destructor TXX_XXXX.Destroy;
    begin
      FreeAndNil( Allocations ); //
      inherited;
    end;
    
    2) 在aOwnsObjects设置为False的情况下创建TObjectList,在这种情况下,您必须自行释放列表中的对象。您的代码可以如下所示:

    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;
    

    “但在这里,在我执行free之前,我检查了分配”…请注意,这仅在您不仅调用
    free
    而且还显式地将引用设置为
    nil
    时才有帮助(或者最好使用
    FreeAndNil
    Free
    不会将引用设置为
    nil
    ,这是
    分配的
    检查的内容!既然他没有把它们拿走,那就没那么重要了。但通常是这样。清除列表释放列表中的元素通常是这样的。但是,如果存在更复杂的所有权模型,那么销毁obj可能会导致列表从自身中删除obj。向零的循环也是稍微优化的,所以无论如何使用它都是个好主意。但是我现在将示例代码改为使用
    Delete
    。我还没有遇到过比这更复杂的所有权模型,在这里很难理解它-为什么使用Delete而不是Free。@spspli如果TObjectList拥有它所包含的对象,那么调用
    Delete
    将从列表中删除该项并将其释放。但是,由于列表中对象的计数发生了变化,您必须将循环降到0,否则您将得到错误,因为for循环索引在循环开始时只计算了一次是“过度杀伤力”,因为您正在禁用一个局部变量。只要
    oObject.Free
    就可以了。我相信鲁迪很快就会教你:)即使对局部变量使用
    FreeAndNil
    ,也不会伤害IMHO,它的好处是人们不必每次都考虑使用哪一个。有关
    Free
    FreeAndNil
    的讨论,请参见此处: