问题
在下面的代码中,创建了一个 TStringList 类型的新对象,并将其传递给使用该对象的过程。通过将对象传递给方法 ProcToFillStringList,通过复制引用来创建新的对象引用。我对这段代码的疑问是:
存储在参数 SList 中的对象引用会发生什么情况 方法返回后?它是否删除了对该对象的引用 从堆栈中?
Free() 方法内部实际上做了什么?它是从堆栈中删除对该对象的所有引用还是删除 物体本身?哪些引用被删除?
当方法返回时,对象引用(而不是对象本身)是否会自动从堆栈中删除?
通过引用传递引用会更好吗?
代码
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 真正设置为 nil。
但在所有其他平台中,对象不进行引用计数。当调用 Free 时,对象内存被释放,但你的变量不会自动设置为 nil (不是说另一个变量指向同一个对象),使用这样的语法是不可能的。对象的任何方法都不能更改调用它的变量。这就是为什么您要编写 SL := TStringList.Create
而不是 SL.Create
。在第一种情况下,您将获得创建对象的新内存地址并将 SL 分配给它。在第二个 SL 中,未初始化并且可以指向任何地方,因此无法准确地在那里创建对象。
那么,回答你的问题:
本地过程中的对象引用超出范围时将被删除。但是,如果您使用 const 或 var 参数,则首先不会创建它。实际上,您在这里使用相同的引用 SL。
在 iOS 中,
Free
不执行任何操作,当 SL 变量超出范围时,对象将自动销毁。在其他平台中,Free 会销毁对象并且根本不影响其他引用。是的,他们确实如此。
使用最能描述您情况的修饰语。
Const
将告诉编译器和使用您的代码的人员(包括您自己),参数在过程中不会更改,编译器可以按值(对于小于指针的对象)或按引用传递它,但不能无论它选择什么,引用计数都不会增加,因此从这个角度来看,您可以认为您使用了完全相同的对象,就像它是通过引用传递的一样。
使用 Var
(通过引用)您可能会意外更改传递给过程的变量,这会使您的意图不清楚,因此仅当您确实想要更改此变量和 Const 时才使用它
否则。
关于delphi - Free() 方法实际上在内部做了什么以及它如何处理对象引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37226384/