【问题标题】:Fundamental of "Free" in DelphiDelphi中的“免费”基础
【发布时间】:2012-08-30 08:53:13
【问题描述】:

我对 Delphi 有一个基本的疑问。当我在设计时保留任何组件时,例如 TADOConnectuion 并单击按钮,即使我编写以下代码也不会出现任何错误:

begin
  ADOConnection.Free;  //No error
  ADOConnection.Free;  //No error
  ADOConnection.Free;  //No error
end;

但如果我在运行时创建与以下相同的对象,我会收到“访问冲突...”错误

begin
  ADOConnection := TADOConnection.create(self);
  ADOConnection.Free;  //No error
  ADOConnection.Free;  //Getting an "Access Violation..." error
end;

即使我创建如下对象,我也会遇到同样的错误:

ADOConnection := TADOConnection.create(nil);

只是想知道这种行为背后的原因,即为什么我在设计时保留组件时没有错误?

【问题讨论】:

标签: delphi delphi-7 delphi-xe2 delphi-2010


【解决方案1】:

如果你释放一个组件,它在所有者中的对应字段将被清除。如果添加设计时ADOConnection,则

ADOConnection.Free; // Frees ADOConnection and sets ADOConnection to nil
ADOConnection.Free; // Does nothing since ADOConnection is nil

您可以通过在变量中捕获它来查看它:

var c: TADOConnection;
c := ADOConnection;
c.Free; // Frees ADOConnection and sets ADOConnection to nil
c.Free; // Error: c is not set to nil

即使在设计时创建了ADOConnection,这也是行不通的。

这是一个带有 TButton 组件的示例,它演示了您在设计时组件中看到的行为并非特定于设计时组件:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  published
    Button: TButton;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Assert(not Assigned(Button));
  TButton.Create(Self).Name := 'Button'; // Button field gets set
  Assert(Assigned(Button));
  Button.Free;                           // Button field gets cleared
  Assert(not Assigned(Button));
  Button.Free;                           // Okay, Free may be called on nil values
end;

end.

【讨论】:

  • 最后一个疑问,为什么设计时和运行时会有这样的行为?
  • @VishalTiwari 设计时和运行时的区别并不在于拥有和不拥有。稍后我会举一个例子。
  • 设置ADOConnection为nil的相关代码在TComponent.SetReference中,与Free没有直接关系,更多的是与RemoveComponent(Destroy内部调用)有关。当所有者拥有一个名称类似于子组件的字段时,此机制起作用。
  • @UweRaabe 实际上,当组件被重命名或删除时,该字段会被清除,这可能是由于其他原因而不是被释放。
  • @VishalTiwari 它要求设置组件的所有者(不是父级),组件具有名称,组件的所有者具有同名的已发布字段,并且您可以访问组件使用该已发布字段。这是移除组件时唯一会自动清除的字段。
【解决方案2】:

ADOConnection 最初是 nil,所以如果你释放它,free 函数不会做任何事情,因为传递给它的指针是 nil。在随后的调用中,指针保持为 nil,因此 free 继续什么都不做。当你用 create 初始化 ADOConnection 时,ADOConnection 中持有的指针不再是 nil,所以第一次调用 free 会主动释放指针,但后续调用会看到内存已经被释放并引发异常。调用 free 不会更改指针。为此,您需要 freeandnil。

【讨论】:

  • 但有时 FreeAndNil 函数也会无缘无故地给出“访问冲突...”错误,这就是我使用 .Free 的原因
  • @VishalTiwari:你真的看过 FreeAndNil 中的代码吗?访问冲突肯定来自某个地方,但我不明白它是如何由 FreeAndNil 而不是由 Free 引起的......
  • @VishalTiwari:这种非零无效引用称为stale pointer。除非析构函数写得如此糟糕以至于它会抛出一个未处理的 AV 异常,否则我记得让 FreeAndNil 抛出一个 AV 的唯一方法是向它传递一个过时的指针......
猜你喜欢
  • 1970-01-01
  • 2011-01-25
  • 2017-08-05
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多