5

当在函数中创建的对象并且函数完成时,如果对象没有被显式销毁,会发生什么?

所有变量在超出范围时都需要销毁,还是在超出范围时得到处理?

例如,调用 custom_function 后 locallist 会发生什么?

function TForm1.custom_function(string: test_string): boolean;
var locallist: TStringList;
begin
  locallist := TStringList.Create;
  // do a bunch of stuff here, but don't destroy locallist
  return true;
end;
4

5 回答 5

17

你会得到一个内存泄漏。

正确的模式是

myObject := TObject.Create;
try
  //do stuff
finally
  myObject.Free;
end;

此外,如果您需要稍后测试对象是否已被释放,请使用 FreeAndNil(myObject)。它也会将变量设置为 nil,因此您可以稍后对其进行测试。

于 2009-03-11T20:07:05.957 回答
13

正如其他海报所指出的,这些对象需要明确释放。这通常是通过使用 try..finally 块手动完成的,正如 Ray 所演示的。但是,您应该注意一些例外情况。

组件(TComponent 后代)将 Owner 参数传递给它们的构造函数。如果所有者不是nil,那么所有者组件将获得新组件的所有权并在它被释放时释放它。这就是为什么您不必清理自己的表单的原因;它们连接到 Application 对象,该对象知道如何在程序完成时释放自己。但是,如果您在运行时创建组件,则需要为其分配所有者或将nil传递给构造函数,然后自己释放它。 不要通过释放具有所有者的组件来混合两者。 在某些情况下,这可能会导致双重自由状态。

如果您将实现引用计数的接口对象(主要是 TInterfacedObject 后代)专门称为接口(而不是对象),则引用计数机制将释放它们。当对它们的最后一个接口引用被删除时,它们会自动释放。如果您已将 TInterfacedObject 分配给接口引用,请不要手动释放它。这将引发异常。此外,请注意并非所有具有接口的对象都实现引用计数。大多数只是从 TInterfacedObject 继承的那些。

创建一个对象,使用 try..finally 块,然后释放它并不总是可行的。有时这对您正在做的事情不起作用,特别是如果您将对象分配给某种列表(并制作大量列表)。在这种情况下,使用 TObjectList 是个好主意(或者更好的是,如果您有 D2009,一个 TObjectList),并且 OwnsObjects 属性设置为true。这会导致列表成为其中对象的所有者,并在释放时释放它们,就像组件一样。同样,如果对象由对象列表拥有,则不要手动释放对象。

动态数组(包括字符串)由具有引用计数系统的编译器管理,大多数其他类型的变量在堆栈上分配。你永远不必担心手动释放对象以外的任何东西,除非你在玩指针。

这可能听起来很复杂,但你很快就会习惯的。请记住,每个对象都属于以下三件事之一:另一个对象、接口引用计数系统或您的代码,并且所有者应该在不再需要时释放其所有对象。任何东西都不应该试图释放属于其他东西的东西。(你不能偷窃。)记住这些指导方针,你最终会得到良好的内存管理。您还可以在 DPR 的主例程中设置“ReportMemoryLeaksOnShutdown := true”以获得更多帮助。

于 2009-03-11T21:06:53.050 回答
5

它成为泄漏的内存。

您通常应该围绕这样的分配:

locallist := TStringList.Create;
try
     // work with locallist here
  finally
     locallist.Free;
  end; 

Delphi 中唯一一种当它们退出范围时自杀的引用是接口引用。

于 2009-03-11T19:27:56.830 回答
3

内存没有回收,抱歉。您需要使用 locallist.Free 显式释放该对象;

于 2009-03-11T19:28:08.873 回答
3

这将导致内存泄漏,只会在应用程序结束时销毁。要检测这些,您可以使用像FastMM这样的内存管理器。

请注意,您可以使用 Destroy 或 Free 方法销毁对象。如果对象为零,第一个将给出错误,后者不会 - 它等于

if Assigned(Object) then Object.Destroy;
于 2009-03-11T19:28:27.170 回答