Delphi中的窗体,有模式窗体与非模式窗体两种。两种窗体的调用方式不同,模式窗体使用ShowModal显示,非模式窗体使用Show显示。当显示模式窗体的时候你是不能操作本程序的其他窗体的,你不能把焦点从模式窗体转到其他窗体上。而非模式窗体则没有这种限制,你可以从一个非模式窗体切换到另外一个非模式窗体上。两种窗体的区别仅仅在调用的方式上有区别。
窗体的创建:
当使用Delphi的IDE New一个窗体后,在Project1.dpr文件中,会出现一句
1
|
Application
.
CreateForm(TForm2, Form2);
|
意思是当程序启动的时候会创建TForm2类的Form2实例,也就是窗体2。这一过程是自动的,不管你是否调用显示Form2,
Form2已经存在了,其OnCreate事件也已经触发了。这时候,如果你想显示Form2,那么可以显式调用Form2的Show或ShowModal方法,以非模式或模式显示窗体。
如果你不想随程序启动而创建窗体,而是动态的创建窗体,那么,你可以删除上面的那句代码,然后在程序中你想动态创建窗体的地方加上以下代码:
1
2
|
//需要在头部引用TForm2所在的单元文件,并声明Form2变量。
Application
.
CreateForm(TForm2, Form2);
|
或者使用
1
2
|
//和上面一样,需要引用并声明。注意,一定不要直接这样写Form2.Create
Form2 := TForm2
.
Create(Application);
|
两者没有特别大的区别,一般情况下是通用的。两者的主要区别在于,触发OnCreate事件的时候,第一种方法Form2变量已经指向了新生成的实例,外部代码可以直接调用Form2这个变量了,而第二种方法先调用TForm2的Create方法,再给Form2这个变量赋值,在OnCreate时Form2变量还是未定义的。这点区别比较小,基本上不会遇到,但是如果不明白其中的道理,又遇到这个问题的话会浪费你很多时间来调试的。
窗体销毁:
有创建,就必须有销毁,不然的话会产生内存泄漏。
当我们调用Form的Close或者点击窗体右上角的X时,窗体真的已经释放了么?答案是否定的,如果你想验证的话……你可以去看源码。对于程序自动生成的窗体,在程序结束的时候会自己释放,对于这种窗体,我不建议进行手动释放,除非你确定释放后绝对不会再用到这个窗体了,不然就会产生内存访问错误。
对于动态生成的窗体,一旦不再使用,一定要手动释放资源,不然你会看到程序占有内存疯狂上涨,然后就被任务管理器干掉了~手动释放主要有两种方式,第一种是显示调用Form.Free这个方法。对于上面用的例子Form2来说:
1
2
|
Form2
.
Free;
Form2 :=
nil
;
|
至于为什么要将变量Form2赋值为nil,是为了防止此后的代码通过Form2变量访问已经被释放的TForm2实例。
另一种方法是在窗体的OnClose事件中将变量Action设置为caFree。
1
2
3
4
5
|
procedure
TForm2
.
FormClose(Sender: TObject;
var
Action: TCloseAction);
begin
Action := caFree;
// TCloseAction = (caNone, caHide, caFree, caMinimize);
Form2 :=
nil
;
end
;
|
这两种方法没有什么本质区别,可以根据实际情况来使用,但是一定不要忘记在释放资源后将变量指向空,不然很容易出现非法访问的情况。实际上在TForm2的FormClose中写Form2 := nil 是无用的,应该写在创建Form2的单元中,因为这两个Form2变量是不同的,TForm2所在单元的Form2变量是一个私有变量,别人是访问不到的,别人可以访问到的,也就是可能触发非法访问的是在TForm2所在单元外的Form2变量(有点绕口令了,不知道大家看懂没有……)。
声明:未作说明,则本文为代码至上原创。转载务必注明出处。
注意:转载须保留全文,如需修改请 联系作者。
本文永久地址:http://codeup.org/archives/192
问题描述:
动态创建form常碰到一个问题就是必须在创建之前判断form是否已经存在,通常第一次创建的时候form肯定为nil,但是当使用完毕之后如果
form.free后,form肯定不是nil,必须在form关闭之后在他的主调用form中动态freeandnil(form),或者form.free;form.nil;才能变成nil
我想知道有没有办法当form.free之后,form指针并不是nil的时候来判断form是否已经被free过了,是否能够正常创建一个新的对象。
这个问题困扰了很久了!
个人感觉楼上各位的方法根本没有针对楼主的问题啊!
当申明一个Form1的时候,只是在栈上创建一个四个字节的内存空间。直到调用构造器的时候才真正跑到堆上分配对象实际内存空间,也只有到了这个
时候,前面分配的四个字节的对象指针的内容才有意义!
当我们释放了Form1的时候,如果不释放栈上的对象指针,就会形成一个野指针,如果对野指针进行操作,马上就会出现一个AV访问错误....楼主的意
思,我想就是在不释放对象指针的情况下去判断当前对象指针指向的对象是否还存在!个人认为没有任何现成的方法可以进行这种判断!
如果使用Release、Free或Action:=caFree等方法将窗体释放掉后,此时窗体指针内容并不改变,这个时候用Assigned来判断虽然也可以判断窗体对
象指针有意义,但实际窗体是根本不存在的!
FrameSniper兄,我想知道的就是这个,难道没有任何办法可以判断一个指针的地址是否真的有效吗?经常碰到这个问题,通常都要很严格的控制一个
指针free后必须nil,或者读取指针的某一个属性来判断有效性,感觉这个办法很笨,感觉肯定有一些好的办法,等到这个问题解决了在接贴吧!
注明:
代码例子如下:
有form1,form2 ,form1打开form2,关闭form2的时候,不释放对象,即不执行Release,那么如下代码,在第2次打开form2的时候,会报错;
if assigned(form2) then
begin
// form2.Free; //--如果form2已经被release后,这步就会报内存访问错误了;
form2 := nil; //--如果没有第一步的free,则将指针置空后,那对象还在堆里,没有被释放掉;
end;
if not Assigned(Application.FindComponent( 'Form2')) then
begin
Form2 := TForm2.Create(Application);
end;
Form2.Show;
-------------------------------------------------------------
正确例子应该是这样:
form2关闭时,要执行release,释放掉对象;
然后在打开form2的时候,以下代码就可以了;
if not Assigned(Application.FindComponent( 'Form2')) then
begin
Form2 := TForm2.Create(Application);
end;
Form2.Show;