问题
I stumbled on this page Why shouldn’t I call Application.CreateForm. Now I have some code like this:
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update; // force update
Application.Initialize;
Application.CreateForm(TClientData, ClientData);
SplashForm.Update; // force update
Application.CreateForm(TClientMainForm, ClientMainForm);
Application.ShowHint := True;
Application.Run;
ClientMainForm.ServerConnected := false;
FreeAndNil(ClientMainForm);
FreeAndNil(ClientData);
First a splashform is created, then a datamodule and last the main form. The page says that Application.CreateForm should not be called twice. Should the code above be changed?
回答1:
There is nothing wrong with using Application.CreateForm multiple times. But this introduces global variables for each form which can be a code smell. Unfortunately the IDE creates one for each form. Although you can remove them if you like.
A better way is to create a form when you need it and release it when you are ready with it. So you only use Application.CreateForm for the main form.
A main datamodule can be created by the main form. But it can be global too, just a matter of taste.
So to answer the question, you can avoid Application.CreateForm by creating and releasing the forms locally.
The article mentions the side effect of Application.CreateForm (the first completed form is the main form). So there can be unexpected side effects if the main form creates other forms using Application.CreateForm.
So just to avoid any nastyness, you should limit yoursef to a single call. Which is done using only one global form.
回答2:
If TClientData is a Data Module and TClientMainForm is a form, then no (except perhaps the two FreeAndNil calls at the end - not really needed). But take care. Because as it says Rob Kennedy in his post, the Application.CreateForm does other things behind (it sets the MainForm variable), so I would advise to set up your project file according to the following rules:
Create all the forms which you want to create at startup using a single call to Application.CreateForm - usually this is done by the IDE.
Remove from the project file the forms which you want to create dynamically (on-demand) in your program. (In Project | Options | Forms...) - move them from 'Auto-Create Forms' to 'Available Forms'
Create your forms in your code using TmyForm.Create(Owner) (etc.) and not with Application.CreateForm(...). As an aside, if you are sure that you will free the form, then it is better (in order to speed the things up) to call TmyForm.Create(nil) - iow without any owner.
If you want to do some kind of initialization at startup you can have a procedure / method in the project file tied to a form / data module already created and run it before application run.
For example:
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TdmoMain, dmoMain); //<--this is a data module
Application.CreateForm(TfrmMain, frmMain); //<--this will became the main form
Application.CreateForm(TfrmAbout, frmAbout);
//... other forms created here...
frmMain.InitEngine; //<--initialization code. You can put somewhere else, according with your app architecture
Application.Run;
end.
In this way you will have the project file clean and you will know exactly which is which.
HTH
回答3:
When I wrote that article, I was thinking primarily of code outside the DPR file. People see the form-creation code generated by the IDE in the DPR file and think that's the best way to create forms generally, so they use that elsewhere in their programs. They sometimes use it in the main form's OnCreate event handler to create other forms their program needs, and then they hit problems because the program's main form isn't what they think it is.
In the code you provided, it's easy to call CreateForm just once. Use it for the main form, and for nothing else. The data module isn't a main form, so you don't need CreateForm's magic there.
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update; // force update
Application.Initialize;
// Change to this.
ClientData := TClientData.Create(Application);
SplashForm.Update; // force update
Application.CreateForm(TClientMainForm, ClientMainForm);
Application.ShowHint := True;
Application.Run;
ClientMainForm.ServerConnected := false;
// Remove these.
FreeAndNil(ClientMainForm);
FreeAndNil(ClientData);
You really shouldn't free the objects you've created here because you don't own them. They're owned by the global Application object, so let it take care of freeing them: Remove the two calls to FreeAndNil.
回答4:
The article you refer to is incorrect. There are a number of valid reasons why you would want multiple calls to Application.CreateForm
1) Datamodules: You probably want these available all of the time. Best way to do this is Application.CreateForm. I know of applications with several themed Datamodules e.g. Customer, Invoice, Address to handle different areas of the database & encapsulate the functionality neatly. All of these are created in the .dpr
2) Big, slow loading stuff (which is a bad idea in & of itself but these things happen and are often inherited by support programmers...). Move the load time into the application startup exactly like your example code along with the splash screen updating. The users expect applications to take a while to get going thanks to the stirling efforts of our collegues working on Microsoft Office to lower the bar of expectations for the rest of us :)
So, in summary, don't worry your code is fine - but you can lose the "FreeAndNil" stuff. However small quick hitting Dialog type stuff is best invoked by:
with TMyform.Create(nil) do
try
//Setup
case ShowModal of
// Whatever return values you care about (if any)
end;
finally
Free;
end;
Short, sweet, to the point & minimises memory usage...
来源:https://stackoverflow.com/questions/2175253/how-do-i-avoid-to-call-application-createform-twice