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

时间:2016-05-14 11:57:54

标签: delphi memory object-reference

问题

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

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

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

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

  4. 传递参考byref会不会更好?

  5. 代码

    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;
    

3 个答案:

答案 0 :(得分:9)

以下是新版本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根本不起作用,只有在删除对它们的最后一个引用时才释放对象(但是如上面代码的注释中所述,编译器会更改您的{ {1}}到SL.Free,所以如果它是对象的最后一个引用,它将被释放,SL实际上设置为nil。

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

所以,回答你的问题:

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

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

  3. 是的,他们这样做。

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

  5. 使用Const(通过引用)您可能会意外地更改传递给过程的变量,这会使您的意图不清楚,因此只有在您真的想要更改此变量时才使用它,否则使用Var

答案 1 :(得分:1)

在embarcadero的文档中写了

  

如果对象引用不是nil

,则System :: TObject :: Free会自动调用析构函数

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

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

public void foo {
    label.setVisible(true);
    //the label is not visible
    foo2();
    //here the label is visible
}

答案 2 :(得分:0)

  

通过复制参考

创建新的对象引用

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

Free并未清除任何引用。它只是破坏了一个物体,释放了它的记忆。有&#39; FreeAndNil&#39;解除对象并使一个引用 nil的例程。其他参考文献仍然存在。