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



问题

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

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

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

  3. 当方法返回时,对象引用(而不是对象本身)是否会自动从堆栈中删除?

  4. 通过ref传递引用会更好吗?

代码

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;

以下是新版本Delphi:上Free方法的代码

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.Free更改为SL:=nil,所以如果它是对对象的最近一个引用,它就会被释放,SL实际上设置为零。

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

因此,为了回答您的问题:

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

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

  3. 是的,他们有。

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

使用Var(通过引用),您可能会意外地更改传递给过程的变量,这会使您的意图不明确,所以只有当您真正想更改此变量时才使用它,否则使用Const

在embacadero的文档中写入

如果对象引用不是nil ,System::TObject::Free会自动调用析构函数

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

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

QueryInterface
_AddRef
_Release

通过应对引用来创建新的对象引用

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

Free未清除任何引用。它只是破坏一个对象,释放它的内存。有一个"FreeAndNil"例程释放对象并使一个引用nil。其他参考文献仍然存在。

最新更新