Free() 方法实际上在内部做了什么以及它如何处理对象引用?

What does the Free() method actually do internally and how does it handle object references?

问题

在下面的代码中,创建了一个类型为 TStringList 的新对象,并将其传递给使用该对象的过程。通过将对象传递给方法 ProcToFillStringList,通过复制引用创建一个新的对象引用。我对这段代码的问题是:

  1. 存储在参数SList中的对象引用发生了什么 在方法 returns 之后?它是否删除了对对象的引用 来自堆栈?

  2. Free() 方法实际上在内部做什么?它是从堆栈中删除对该对象的所有引用还是删除 对象本身?删除了哪些引用?

  3. 当方法 returns?

  4. 时,对象引用(不是对象本身)是否会自动从堆栈中删除
  5. 引用byref传递会不会更好?

代码

var
  SL: TStringList; // first object reference
begin
  SL := TStringList.Create; // creating object
  try
    ProcToFillStringList(SL);
  finally
    SL.Free; // -> what gets 'freed' here? the object? the references? both?
  end;
end;

procedure ProcToFillStringList(const SList: TStrings); // second object reference
  SList.Add('x'); // not calling Free -> does the reference get removed?
end;

a new object reference is created by coping the reference

新引用 const SList 只是指向对象的不可更改的指针。如果它存在于堆栈中,它将被从堆栈中删除(在这种情况下参数通过寄存器传递)。

Free 不清除任何引用。它只是破坏一个对象,释放它的内存。有 'FreeAndNil' 释放对象并生成 一个引用 nil 的例程。其他引用仍然存在。

在 embarcadero 的文档中写道

System::TObject::Free automatically calls the destructor if the object reference is not nil

这意味着在您的情况下对象 SL 在您调用 SL.Free 时被清除。从 TObject 继承的对象不知道有多少引用对该实例有效。只有指向 SL 实例地址的指针被传递给函数调用 ProcToFillStringList。实例不会被告知新引用。

如果您想处理引用计数,请查看 TInterfacedObject 和 3 种方法

QueryInterface
_AddRef
_Release

这是 Free 方法在较新版本 Delphi 上的代码:

procedure TObject.Free;
begin
// under ARC, this method isn't actually called since the compiler translates
// the call to be a mere nil assignment to the instance variable, which then calls _InstClear
{$IFNDEF AUTOREFCOUNT}
  if Self <> nil then
    Destroy;
{$ENDIF}
end;

有两种不同的情况。当编译到具有自动引用计数的环境(即 iOS)时,Free 根本不起作用,只有在删除对它们的最后一次引用时才释放对象(但如上面代码的注释中所述,编译器更改你的 SL.FreeSL:=nil,所以如果它是对象的最后一个引用,它将被释放并且 SL 真的设置为 nil。

但在所有其他平台中,对象不被引用计数。当调用 Free 时,对象内存被释放,但你的变量不会自动设置为 nil(不是说另一个变量指向同一个对象),用这样的语法是不可能的。对象的任何方法都不能更改调用它的变量。这就是为什么你写 SL := TStringList.Create 而不是 SL.Create 的原因。在第一种情况下,您将获得创建对象的新内存地址并将 SL 分配给它。在第二个 SL 没有初始化并且可以指向任何地方,所以没有办法在那里创建对象。

所以,回答你的问题:

  1. 本地过程中的对象引用超出范围时将被删除。但是,如果您使用 constvar 参数,则它不会首先创建。实际上,您在这里使用的是相同的参考 SL。

  2. 在iOS中Free什么都不做,当SL变量超出范围时对象会自动销毁。在其他平台中,Free 会销毁对象并且根本不会影响其他引用。

  3. 是的,他们有。

  4. 使用最能描述您的情况的修饰符。 Const 会告诉编译器和使用你的代码的人(包括你自己)参数不会在过程中改变,编译器可以通过值(对于小于指针的对象)或引用传递它,但不管它是什么选择,refcount 永远不会增加,所以从这个角度来看,你可以认为你使用了完全相同的对象,就像它是通过引用传递的一样。

使用 Var(通过引用)您可能会不小心更改传递给过程的变量,这会使您的意图不明确,因此只有在您真正想要更改此变量时才使用它 Const否则。