What is the correct way to dynamically create/release runtime forms?
Asked Answered
G

5

18

I always try to create my Applications with memory usage in mind, if you dont need it then don't create it is the way I look at it.

Anyway, take the following as an example:

Form2:= TForm2.Create(nil);
try
  Form2.ShowModal;
finally
  Form2.FreeOnRelease;
end;

I actually think Form2.Destroy is probably the better option, which brings me to my question..

What is the difference between calling:

Form2.Destroy;
Form2.Free;
Form2.FreeOnRelease;

They all do the same or similar job, unless I am missing something.

And also when should any of the above be used? Obviously when freeing an Object I understand that, but in some situations is Destroy better suited than Free for example?

Gereld answered 10/6, 2011 at 18:41 Comment(2)
read up on TObject.Free, TObject.DestroyGoldeneye
calling Free checks if the object is not Nil and then call Destroy accordingly. It's safe. calling Destroy does not check if involved object is nil so it might trigger VA.Mandragora
W
16

Form2:= TForm2.Create(nil);

This is a code-smell, because Form2 is probably the global, IDE-generated variable that would normally hold an IDE-created TForm2. You most likely want to use a local variable, and one with a better name. This is not necessary an error, just a code-smell.

Form2.Destroy vs Form2.Free

Use Form2.Free, because it calls Destroy anyway. You can CTRL+Click on the name (Free) to see it's implementation. Essentially Free calls Destroy if Self is not nil.

Form2.FreeOnRelease

As the documentation says, "It should not be necessary to call FreeOnRelease directly."

Woodley answered 10/6, 2011 at 18:55 Comment(5)
You forgot to mention the (IMHO) most important question, the one of Free/Release.Cupid
+1 because of the word of caution about the global variable, though.Cupid
I used Form2 as an example, I always give forms/components meaningful names such as frmMain, frmAbout, cmdOK, cmdCancel etc. It seems from a few comments FreeOnRelease is best avoided, and Free should be the way to go.Gereld
@Craig: The point isn't that Form2 is a non-descriptive name. The issue is that you should not use the global var Form2: TForm2 variable in the interface section of Unit2 if you do the try ShowModal finally Free thing. Instead, you should use a local variable in such a case. You can name it Form2 if you'd like.Cupid
ok I understand now thanks Andreas, it makes more sense doing this.Gereld
C
10

I've never actually heard of FreeOnRelease before. A quick Google search turned up the reason why. From the official documentation:

FreeOnRelease is called when an interface implemented by the component is released. FreeOnRelease is used internally and calls the corresponding interface method. It should not be necessary to call FreeOnRelease directly.

As for Free vs. Destroy, Free is a safety feature. It's basically implemented as if self <> nil then self.Destroy;, and it was created to make constructors and destructors safe to use. Here's the basic idea:

If you're constructing an object and an unhandled exception is raised, the destructor gets called. If your object contains other objects, they may or may not have been created yet by the time the error occurred, so you can't just try to call Destroy on all of them. But you need a way to make sure that the ones that have been created do get destroyed.

Since Delphi zeros out the address space of an object before calling the constructor, anything that hasn't been created yet is guaranteed to be nil at this point. So you could say if FSubObject <> nil then FSubObject.Destroy again and again for all the sub-objects, (and if you forget that you're going to get access violations,) or you can use the Free method, which does it for you. (This is a huge improvement over C++, where the memory space is not zeroed before the constructor is called, which requires you to wrap all your sub-objects in smart pointers and use RAII to maintain exception safety!)

It's useful in other places as well, and there's really no reason not to use it. I've never noticed that Free imposes any measurable performance penalty, and it improves the safety of your code, so it's a good idea to use it in all cases.

Having said that, when dealing with forms specifically, there's an additional variable to factor into the equation: the Windows message queue. You don't know if there are still pending messages for the form you're about to free, so it's not always safe to call Free on a form. For that, there's the Release method. It posts a message to the queue that causes the form to free itself once it's got no more messages to handle, so it's generally the best way to free a form you no longer need.

Coorg answered 10/6, 2011 at 18:51 Comment(0)
O
6

The canonical form is:

Form := TMyForm.Create(nil);
try
  Form.ShowModal;
finally
  Form.Free;
end;

Never call Destroy, always call Free instead.

FreeOnRelease is a total red herring. Sometimes, if there are queued messages destined for your form or its children, then you might elect to call Release although often that's indicative of design problems.

Osmosis answered 10/6, 2011 at 19:9 Comment(7)
And what part of this answer hasn't already been covered by the previous answers? :)Cupid
@Andreas My answer is the most concise and to the point. The other answers made my eyes glaze over but I am a child of the times and can't concentrate on large amounts of text! ;-)Osmosis
still useful additional info.Gereld
There are a number of thinkable circumstances in which Release is perfectly sensible, the most notable being destroying a Form from within itself. But it sure is wise to study any need of Release.Quentinquercetin
+1 for NGLN's comment. You can never call Free from within that form (e.g. a button click); that in an access violation seconds away from happening. But if you have a situation where you want to destroy a form that you're on, you can call Release, and it will be freed when it's safe to do so. Since nobody ever wants to release a form out from under themselves - the canonical form is best.Submission
@ian when it's safe to do so is too strong. Release can't guarantee that. For example the messages in the queue might post other messages. There are situations where Release is valuable but it greatly complicates reasoning about your flow and I always avoid using it if I can.Osmosis
Any messages still in the queue after their destination windows has been destroyed will not go anywhere. No harm no foul.Submission
C
5

The idiomatic usage is

procedure SomeProc;
var
  frm: TForm2;
begin
  frm := TForm2.Create(nil);
  try
    frm.ShowModal;
  finally
    frm.Free;
  end;
end;

or, unless you hate the with construct,

with TForm2.Create(nil) do
  try
    ShowModal;
  finally
    Free;
  end;

You should never call Destroy, according to the documentation. In fact, Free is exactly equivalent to if Self <> nil then Destroy;. That is, it is a 'safe' version of Destroy. It doesn't crash totally if the pointer happens to be nil. [To test this, add a private field FBitmap: TBitmap to your form class, and then OnCreate (for instance), try FBitmap.Free vs. FBitmap.Destroy.]

If you create the form using the approach above, Free is perfectly safe, unless you do some strange things in the form class.

However, if you use CreateForm(TForm2, Form2) to create the form and store the form object in the global instance variable Form2 and you don't free it immediately [for instance, if you want the window to stick around next to the main form in a non-modal way for a few minutes], you should probably use Release instead of Free. From the documentation,

Release does not destroy the form until all event handlers of the form and event handlers of components on the form have finished executing. Release also guarantees that all messages in the form's event queue are processed before the form is released. Any event handlers for the form or its children should use Release instead of Free (Delphi) or delete (C++). Failing to do so can cause a memory access error.

FreeOnRelease has nothing in particular do to with forms. From the docs:

It should not be necessary to call FreeOnRelease directly.

Cupid answered 10/6, 2011 at 18:52 Comment(0)
B
0

the other way is passing caFree to Action of formonclose

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end
Buttermilk answered 13/2, 2018 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.