function IsNilTheForm(var TheForm: TMyForm);
begin
if nil=TheForm
then begin // The form was freed and nil
IsNilTheForm:=True; // Return value
end
else begin // The form refence is not nil, but we do not know is it has been freed or not
try
if TheForm.PleaseFreeAndNilMe
then begin // The form is not freed but wants to
try
TheForm.Free;
except
end;
try
TheForm:=Nil;
except
end;
IsNilTheForm:=True; // Return value
end
else begin // The form is not nil, not freed and do not want to be freed
IsNilTheForm:=False; // Return value
end;
except // The form was freed but not set to nil
TheForm:=Nil; // Set it to nil since it had beed freed
IsNilTheForm:=True; // Return value
end;
end;
end;
所以你在做if nil=MyForm then ...的地方,现在你可以做if (IsNilTheForm(MyForm)) then ...。 就是这样。 计时器解决方案更好,因为窗体会尽可能快地释放(使用的内存更少),而PleaseFreeAndNilMe技巧是在调用IsNilTheForm之前不释放窗体(如果您不在其他任何地方释放它)。 IsNilTheForm是如此复杂,因为它考虑了所有状态(对于多插槽主板和线程应用程序),并让代码在其他任何地方自由/零它。 当然,该函数必须在主线程上和原子排除中调用。 释放一个窗体并使其指针为空不是一件小事,大多数情况下用户可以随时关闭它(因为窗体中没有代码被触发)。 最大的问题是:当用户关闭窗体时,没有办法让事件处理程序在窗体外触发,并且在窗体结束它正在做的所有事情之后。 现在Imaigne的编码器已经放了很多Application.ProcessMessages;在应用程序上的每一个地方,也在那个表单上,等等......并且没有注意到竞态条件......在用户要求关闭这样的表单后,尝试释放和清空它......这是一个噩梦,但是可以通过TForm的黑客版本来解决,该版本有一个变量,告诉用户表单没有被释放,但是需要它。 现在假设你使用了一个被破解的TForm,并且想要一个普通的TForm,只需将它定义为...= class(Forms.TForm),这样它就有了额外的变量。,所以调用IsNilTheForm将相当于与nil进行比较。 希望这能帮助VCL程序员修复这样的问题,比如当一个对象被销毁,释放,niled,隐藏等时引发一个事件......在那个对象的代码之外,比如在主窗体上,等等。这会让生活更轻松......或者只是修复它......关闭和释放意味着将所有指向它的引用都设置为Nil。 还有一件事可以做(但我总是尽量避免):有多个变量指向完全相同的形式(而不是它的副本),这是很容易发生很多错误,你释放一个,并需要零所有他们,等等...我所显示的代码也与之兼容。 我知道代码是复杂的...但自由和零的形式比我的代码更复杂。
5条答案
按热度按时间0kjbasz61#
可以使用Form1.Showing查看窗体是否已关闭。
仅关闭窗体并不能释放它,除非在
OnClose
事件中设置Action := caFree
。默认值为caHide
。y4ekin9u2#
哇,来自过去的爆炸:)
Assigned()的工作原理是,它基本上对指针进行nil检查。如果你销毁了form2,仍然会有一个form2指向的内存地址。
我已经很长时间没有做过 Delphi 了,但是在内存中,你需要在销毁form2变量时手动将其设置为nil。如果你有一个中心位置(例如表单代理?),可以在那里创建和销毁表单,这应该很容易。
iqih9akk3#
如果你使用
Form1.Free
或Form1.Destroy
, Delphi 将销毁对象,但不会将对象引用设置为nil。有关详细信息,请查看this link中的Andreas Rejbrand答案
3bygqnnd4#
在关闭应用程序时遇到了同样的问题。在这种情况下,所有表单都在后台销毁,但指针没有设置为空。以下代码帮助了我:
指针变为空,我可以用
Assigned
检查它,或者与nil
比较。kuarbcqp5#
作为一个提示,在一些特殊情况下,正确的方法是创建一个计时器,为变量赋值nil。
我会解释它(不知何故,它是复杂的),如果你在自己的代码
MyForm:=TMyForm.Create
中创建你的表单,你有一个MyFrom.Close
,这是非常容易的,只是添加一个MyForm:=nil
或更好的MyForm.FreeAndNil
...但有时参考不是anyware。示例:你在一个过程中,在一个循环中创建了相同窗体的许多副本(或只有一个),让窗体打开并结束该过程,现在对打开窗体的引用无处可寻,所以你不能以正常的方式赋值nil或执行freeandnil等操作。
在这种情况下,技巧是使用一个计时器(只有一毫秒)来完成它,该计时器需要引用,所以你必须存储在一个全局变量上,比如对Self的引用,所有这些都可以在on close事件上完成。
最简单的释放方法(当没有引用时)是在主表单上创建一个TObjectList,这样它将保存所有需要释放的表单引用,并定义一个计时器(1毫秒),它将遍历该列表执行freeandnil;然后在onlcose上将
Self
添加到该列表并启用该计时器。现在是另一部分,您有一个在启动时自动创建的标准表单,但您需要将其设置为nil,然后在自己的代码中重新创建它。
这种情况下有一个全局指向该窗体,所以你只需要释放和零它,但不是(我说大声)在自己的窗体代码内的任何部分,你必须这样做(我说如果大声)的窗体代码。
有时候你需要释放表单,当用户关闭它时,它不会以模态显示,这种情况很复杂,但是同样的技巧是有效的,在onclose事件上你启用一个计时器(即超出该窗体,通常在主窗体上),并且计时器将空闲并归零。计时器间隔可以设置为仅一毫秒,它将不会运行,直到窗体完全关闭(请记住不要使用Application.ProcessMessages,这通常是一个非常糟糕主意)。
如果你把Self设置为nil、free或者在自己的表单中的其他值,你可能会破坏你的应用程序内存(这样做是完全不安全的,更不用说它会吃掉内存)。
释放窗体(并使其引用为空)的唯一方法是编写一个触发器,使其在窗体完全关闭后执行此操作。窗体不显示为模态,关闭它的是用户。
我知道如何设置动作来完成自由,但要将其设置为零,没有其他安全的方法。
必须说明:如果你在主窗体上使用计时器,在Onclose事件中对所有计时器运行
Enabled:=False
...否则可能会发生奇怪的事情(不是总是,但有时...关于破坏应用程序和在计时器上运行代码的竞态条件),当然,如果某个计时器被启用,正确地终止它或中止它,等等。您的问题是要做的复杂事情之一......释放和清空一个不是由代码而是由用户操作关闭的表单。
对于所有其他:想象一下,如果应用程序同时打开了许多表单,并且所有表单都可以同时与用户交互(任何人都是模态的),并且您有代码从其他表单引用其中的一些表单...您需要知道用户是否关闭了任何表单,以避免从代码访问该表单。除非使用计时器,否则这一点很重要。
如果你有一个"中央"窗体(如MDI应用程序),你可以把计时器放在主MDI窗体上,这样任何关闭的子窗体都可以被释放和清空,技巧还是在主窗体上放置计时器。
只有当你确信你可以释放和清空所有不可见的表单时,你才可以在主表单上设置一个计时器,如果Visible为false,那么调用FreeAndNil,我认为这种方式容易出错,因为如果你在将来添加一个表单,它不能被释放,但可以保持隐藏...这段代码将是无效的。
请始终记住,如果用户是关闭必须释放的表单的唯一用户,并且表单为空,则代码无法检测并执行操作,也不会启动任何事件(在窗体完全关闭之后)和在窗体完全关闭之前,您甚至不能尝试释放它或使其引用为空,可能会发生奇怪的事情(如果主板有多个插座,更容易出现这种情况,如果你的应用程序使用线程等,也更容易出现这种情况)。
因此,对于线程应用程序(也不线程)我使用另一种方法,工作很好,不需要计时器,但需要在每个
ThatForm.*
之前进行双重检查,技巧是在窗体的公共部分定义一个窗体bolean公共变量,如PleaseFreeAndNilMe
,然后在onclose(作为最后一行)将其设置为True,并在OnCreate将其设置为False。这样,您就可以知道该窗体是已关闭还是仅隐藏(要隐藏窗体,不要调用close,只需调用hide)。
因此,代码将如下所示(您可以将其用作一个变形器,而不是将表单定义为TForm,将其定义为TMyform,或者更好的是,使用类似
type TForm=class(Forms.TForm)
的方法,而不是TMyForm=class(TForm)
,将该变量添加到所有表单中):如果你喜欢黑客版本:
但是正如我所说的,在访问任何成员之前(或者只是在你做nil比较的地方),只需要调用一个'global'函数来传递引用(不管它是否为nil),代码如下:
所以你在做
if nil=MyForm then ...
的地方,现在你可以做if (IsNilTheForm(MyForm)) then ...
。就是这样。
计时器解决方案更好,因为窗体会尽可能快地释放(使用的内存更少),而
PleaseFreeAndNilMe
技巧是在调用IsNilTheForm
之前不释放窗体(如果您不在其他任何地方释放它)。IsNilTheForm
是如此复杂,因为它考虑了所有状态(对于多插槽主板和线程应用程序),并让代码在其他任何地方自由/零它。当然,该函数必须在主线程上和原子排除中调用。
释放一个窗体并使其指针为空不是一件小事,大多数情况下用户可以随时关闭它(因为窗体中没有代码被触发)。
最大的问题是:当用户关闭窗体时,没有办法让事件处理程序在窗体外触发,并且在窗体结束它正在做的所有事情之后。
现在Imaigne的编码器已经放了很多Application.ProcessMessages;在应用程序上的每一个地方,也在那个表单上,等等......并且没有注意到竞态条件......在用户要求关闭这样的表单后,尝试释放和清空它......这是一个噩梦,但是可以通过TForm的黑客版本来解决,该版本有一个变量,告诉用户表单没有被释放,但是需要它。
现在假设你使用了一个被破解的TForm,并且想要一个普通的TForm,只需将它定义为
...= class(Forms.TForm)
,这样它就有了额外的变量。,所以调用IsNilTheForm
将相当于与nil进行比较。希望这能帮助VCL程序员修复这样的问题,比如当一个对象被销毁,释放,niled,隐藏等时引发一个事件......在那个对象的代码之外,比如在主窗体上,等等。这会让生活更轻松......或者只是修复它......关闭和释放意味着将所有指向它的引用都设置为Nil。
还有一件事可以做(但我总是尽量避免):有多个变量指向完全相同的形式(而不是它的副本),这是很容易发生很多错误,你释放一个,并需要零所有他们,等等...我所显示的代码也与之兼容。
我知道代码是复杂的...但自由和零的形式比我的代码更复杂。