Delphi类变量在类析构函数被调用之前就超出了作用域。

5

在类变量中使用动态数组来存储需要在类析构函数被调用时释放的对象是行不通的。

在类析构函数被调用前,该数组似乎已超出其作用域并且已经被处理掉。

这是设计上的问题吗?

在XE5中测试的示例:

type
  TLeakingObject = class
  public
    I : Integer;
  end;

  TTheLeakOwner = class
  public
    class var OutofScopeArray:array of TLeakingObject;
    procedure Add;
    class destructor Destroy;
  end;

procedure TestThis;
var LeakingTest : TTheLeakOwner;
begin
  LeakingTest := TTheLeakOwner.Create;
  try
    LeakingTest.Add;
  finally
    LeakingTest.DisposeOf;
  end;
end;

{ TTheLeakOwner }

procedure TTheLeakOwner.Add;
begin
  setlength(OutofScopeArray, length(OutofScopeArray) + 1);
  OutofScopeArray[length(OutofScopeArray) - 1] := TLeakingObject.Create;
end;

class destructor TTheLeakOwner.Destroy;
var I: Integer;
begin
  // Length(OutofScopeArray) always = 0, gone out of scope before class destructor ??
  for I := 0 to Length(OutofScopeArray) - 1 do
    FreeAndNil(OutofScopeArray[i]);
end;

不是答案,而是一些建议:不要使用数组来存储对象,而是使用TObjectList<T>(generics.collections),当列表超出范围时,它会自动释放对象。 - whosrdaddy
是的,我知道,但这个泄漏发生在一个开源线程池库中,所以我想知道这是否是语言意图,然后再深入研究。 - jong
不用担心,在类析构函数之前,变量已被清除,详见我的回答。 - whosrdaddy
2个回答

5
类析构函数在单元结束之后被调用,这意味着在类析构函数被调用时,数组已经不存在了。在单元结束时,RTL会清理所有托管变量。最终,这并不重要,因为它不是真正的泄漏。
Allen Bauer在这里提供了有关类构造函数/析构函数的更多信息。
编辑:显然,这是按设计来实现的。 这里有更多信息。

谢谢提供这个信息,我会将它改为TObjectList。 - jong

1
这个“设计”已经被固定了。Delphi 10 Seattle的行为与所有人(包括你和我)期望的一样-在类析构函数后,它释放引用计数的类变量。可能Embarcadero的某个人最终意识到另一种方式绝对不酷,特别是在移动平台上使用ARC :D。请参见:https://quality.embarcadero.com/browse/RSP-11289

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接