当我释放Delphi中可能不存在的内存时会发生什么



Delphi没有垃圾收集器,所以对于Java背景的人来说,这是一个真正的痛苦。

通常,要删除不再使用的内存,我会使用:

if (SomeMemory <> nil) then
  SomeMemory.Free

如果我在删除之前没有检查nil会发生什么?

另外,为什么有人想要自己处理所有这些"垃圾收集"?为什么在Delphi提供的所有编译选项中,没有Garbage Collector = true

TObject.Free的代码如下:

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

所以不需要检查nil。但是,如果您尝试释放未初始化的变量,仍然可能会遇到麻烦,因为这将导致AV。检查nil(或Assigned)显然也没有帮助。但是,如果您尝试这样做,编译器将警告您。

回答你的第二个问题

为什么在Delphi所有的编译选项中,没有Garbage Collector = true

简单的答案是Delphi没有这样的垃圾收集器。某些托管类型(如字符串、动态数组等)实现了编译器管理的自动引用计数,当这些对象的引用计数降至零时,它们将被自动释放。对于所有其他非托管对象,开发人员有责任在不再需要对象实例时适当地清理它们。

这不是想要管理应用程序内存的问题,这只是你需要做的事情。

Free之前检查nil是多余的。如果引用是nil,那么在它上面调用Free就已经安全了。如果引用不是 nil,那么在它上面调用Free的安全性是不变的,完全取决于变量是否包含一个有效的引用。

例如:

SomeMemory := nil;
SomeMemory.Free; // This is safe.
SomeMemory := TObject.Create;
SomeMemory.Free; // This is safe
Assert(SomeMemory <> nil);
SomeMemory.Free; // This is an error (EInvalidOperation)

在第一个代码块中,我们不检查变量是否为null,但是对Free的调用是完全安全的。它什么也不做。在第二个块中,我们看到变量仍然是非空的,但是对它调用Free会产生一个异常。

在Delphi中有一种非常直接的方式来进行(类似于)垃圾收集:使用接口指针。通过使用最终从IInterface(或IUnknown,基本上是一样的东西)派生的类型的变量,Delphi将保持引用的密切计数,并在最后一个引用被删除时释放/销毁/释放实例。使用接口指针,除了创建实例和其他一些事情之外,几乎与使用对象引用相同。

在Nick Hodges最近的著作《Coding In Delphi》中,有相当多关于这种编程技术的内容,以及它与抽象、泛型、单元测试和依赖注入的关系。

其他平台的Delphi新版本也将对对象引用进行ARC(自动引用计数),操作方式与现在的接口类似。(废弃Free方法,但那是另一回事。)

为了回答这样的问题,您可以随时查阅文档。在这里,我强调一下:

使用Free来销毁对象。如果对象引用不是nil, Free会自动调用析构函数。任何在运行时实例化的没有所有者的对象都应该通过调用Free来销毁,这样就可以正确地处理它并释放它的内存。与Destroy不同,即使对象为nil, Free也是成功的;如果对象从未初始化,Free不会导致错误。

在这里找到关于这个问题的更完整的讨论:为什么我不能使用"if Assigned()"在使用或释放东西之前?

最新更新