01年本人发表了一篇文章,名为“探究:如何判断Delphi中的对象指针是否可用”。在文中,提出了采用对已经释放的对象进行有选择的属性/方法的访问或调用,然后以是否出现异常来判断其对象指针是否可用。
这种方法荒谬之极!我想以如下程序的运行现象来帮助各位做出明智的判断。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
btn1: TButton;
btn2: TButton;
btn3: TButton;
procedure btn1Click(Sender: TObject);
procedure btn2Click(Sender: TObject);
procedure btn3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.btn1Click(Sender: TObject);
var
a: TComponent;
s: String;
begin
//Do Create a comonent and free it.
a := TComponent.Create(Self);
a.Name := 'a_Name';
a.Tag := 1111;
s := a.GetNamePath;
a.Free;
//Try to access object's method or propery.
Assert(a.Name <> 'a_Name'); //成立!理由是字符串被释放后,其长度变为0,因此为空串
Assert(a.Name = '');//成立!
Assert(a.Tag = 1111);//成立!
Assert(a.ComponentState = [csDestroying, csAncestor]);//成立!
a.FindComponent('');//通过!内部有对象访问的保护。
a.Name := 'NewName'; //异常!Exception.Name = EAccessViolation; Exception.Message: 'access violation at address 0000000. Read of address 00000000'
a.GetParentComponent;//异常!代码停留在 sytem.GetDynaMethod。"access violation ... read of Address 00000007."
Assert(a.GetParentComponent = Self);//异常!代码停留在 sytem.GetDynaMethod。"access violation ... read of Address 00000007."
Assert(a.GetNamePath = s); //异常!代码停留在 sytem.GetDynaMethod。"access violation ... read of Address 00000007."
end;
procedure TForm1.btn2Click(Sender: TObject);
var
a, b: TComponent;
begin
//Do Create a comonent and free it.
a := TComponent.Create(Self);
a.Name := 'a_Name';
a.Tag := 1111;
a.Free;
//Assert it.
b := TComponent.Create(Self);
b.Name := 'b_Name';
b.Tag := 2222;
try
Assert(a = b); //成立!为什么?
finally
b.Free;
end;
end;
procedure TForm1.btn3Click(Sender: TObject);
var
a, b, c: TComponent;
s: String;
begin
a := TComponent.Create(Self);
a.Name := 'a_Name';
a.Tag := 1111;
s := a.GetNamePath;
a.Free;
c := TButton.Create(Self);
b := TComponent.Create(Self);
b.Name := 'b_Name';
b.Tag := 2222;
try
Assert(a <> b);
Assert(a <> c);
Assert(a.Name <> 'a_Name'); //成立!等于原来值的可能性极小!
Assert(a.Name <> '');//成立!理由:为空的可能性极小,但也不是没有可能。
Caption := a.Name; //Caption 为一堆乱码!
Assert(a.Tag <> 1111);//成立!
Caption := IntToStr(a.Tag); //Caption 为一个随机整数。
Assert(a.ComponentState = []);//成立!
a.FindComponent(''); //通过!内部有对象访问的保护。
a.Name := 'NewName'; //异常!停留在 Controls.procedure TControl.Click; 处。Exception.Name = EAccessViolation; Exception.Message: 'access violation at address 00200041. Read of address 00200041'
a.GetParentComponent;//异常!EAbstractError. 停留在 SysUtils单元的procedure AbstractErrorHandler;处。 与上面类似,read of Address 00000007.
Assert(a.GetNamePath <> s); //通过!
Caption := a.GetNamePath; //Caption 为'TFont'。为什么?
finally
b.Free;
c.Free;
end;
end;
end.
综上所述,对象指针的后续使用,依赖于该对象释放之后程序的具体行为。若恰好有一个相同类型的对象马上创建,则很可能两者刚好相同。而如果后续未创建任何对象,以及未申请内存,则部分对象方法或属性其具有特定的值。倘若后续内存有些别样的变化,则结果又不相同。其机理是delphi对对象的创建和销毁的内存管理的问题——想要简单地采用某种方式进行指针是否可用的判断是多么的肤浅和无知啊。