Delphi Free()方法实际上在内部做什么?它如何处理对象引用? 问题:

Delphi Free()方法实际上在内部做什么?它如何处理对象引用? 问题:,delphi,memory,object-reference,Delphi,Memory,Object Reference,在下面的代码中,创建了一个类型为TStringList的新对象,并将其传递给使用该对象的过程。通过将对象传递给方法ProctOffillStringList,将通过引用创建一个新的对象引用。关于此代码,我的问题如下: 存储在参数SList中的对象引用会发生什么变化 在方法返回之后?它是否删除对对象的引用 从堆栈中 Free()方法实际上在内部做什么?它是从堆栈中删除对对象的所有引用还是删除 对象本身?删除了哪些引用 当方法返回时,对象引用(而不是对象本身)是否会自动从堆栈中删除 通过引用byre

在下面的代码中,创建了一个类型为TStringList的新对象,并将其传递给使用该对象的过程。通过将对象传递给方法ProctOffillStringList,将通过引用创建一个新的对象引用。关于此代码,我的问题如下:

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

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

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

  • 通过引用byref是否更好

  • 代码 新的对象引用是通过引用创建的

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


    Free
    不清除任何引用。它只是破坏一个对象,释放它的内存。有一个“FreeAndNil”例程可以释放对象并进行一次引用
    nil
    。其他参考文献仍然存在。

    在embarcadero的文件中是这样写的

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

    这意味着在您的情况下,对象SL在您称为SL.Free的点被清除。从TObject继承的对象不知道有多少引用是该实例的活动引用。只有指向SL实例地址的指针被传递给函数调用ProctOffillStringList。实例未被告知有关新引用的信息

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

    QueryInterface
    _AddRef
    _Release
    

    以下是更新版本的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;
    
    procedure-TObject.Free;
    开始
    //在ARC下,由于编译器进行翻译,因此实际上不会调用此方法
    //该调用仅是实例变量的nil赋值,然后实例变量调用_InstClear
    {$IFNDEF AUTOREFCOUNT}
    如果Self为零,则
    破坏;
    {$ENDIF}
    结束;
    
    有两种不同的情况。当编译到具有自动引用计数(即iOS)的环境中时,Free根本不起作用,只有在删除对对象的最后一个引用时,对象才会被释放(但正如上面代码注释中所述,编译器将您的
    SL.Free
    更改为
    SL:=nil
    ,因此,如果它是对对象的最后一个引用,它将被释放,并且SL实际上设置为nil

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

    因此,要回答您的问题:

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

  • 在iOS
    Free
    中,当SL变量超出范围时,对象将自动销毁。在其他平台中,Free将销毁对象,并且完全不影响其他引用

  • 是的,有

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


  • 使用
    Var
    (参考)您可能会意外地更改传递给过程的变量,这会使您的意图不明确,因此仅当您确实希望更改此变量时才使用它,否则请使用它。

    您是否查看了TObject.Free in System.Pas和TStringList.Free in class.Pas的源代码?您是说要从中删除引用吗堆栈,但那没有任何意义。我不确定你对堆栈的理解是否准确。@David:也许你是对的。你知道我在哪里可以更好地理解其基本概念吗?不确定。所有存在于堆栈上的东西都需要一些修正。对象存在于堆上。引用可以在堆栈上,也可以在堆栈上r heap.const参数只有在大于指针时才通过引用传递。所以您对第4项的回答是错误的。@DavidHeffernan我是否正确理解,即使通过值传递的const参数也不会增加引用计数,无论是字符串、接口还是ARC环境中的对象?无论如何,我现在就更正第4项的答案。只需nt在这方面也很清楚。const参数的ref count没有增加,因为编译器知道它们不能修改。参数是按值传递还是按ref传递是一个不相交的问题
    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;