Solved

Free ListBox-Objects before closing the application

Posted on 2001-09-14
13
195 Views
Last Modified: 2010-04-06
0
Comment
Question by:NicoleKabitzki
  • 4
  • 3
  • 3
  • +2
13 Comments
 
LVL 22

Expert Comment

by:Mohammed Nasman
ID: 6482106
what do u want exactly?
0
 
LVL 8

Accepted Solution

by:
TOndrej earned 200 total points
ID: 6482164
you can do it in the form's OnDestroy:

procedure TForm1.ClearListBoxItems;
var
  I: Integer;
  MyObject: TObject;
begin
  with ListBox1.Items do
    for I := 0 to Count - 1 do
      Objects[I].Free;
end;

procedure TForm1.FormDestroy; // OnDestroy event handler
begin
  ClearListBoxItems;
end;
0
 
LVL 1

Expert Comment

by:malsoft
ID: 6482274
Just to expand a little on TOndrei's comment, I would add some exception handling into the clearing code, just in case something goes awry:

procedure TForm1.ClearListBoxItems;
var
  I: Integer;
begin
  with ListBox1.Items do
    for I := 0 to Pred(Count) do
      try
        Objects[I].Free;
      except
      end;
end;
0
Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

 

Author Comment

by:NicoleKabitzki
ID: 6482354
0
 

Author Comment

by:NicoleKabitzki
ID: 6482355
0
 

Expert Comment

by:duti
ID: 6485553
A constructive comment on response above.

Normally it's not a good idea to eat exceptions.  Exceptions indicate problems, and they are very usefull when debugging. Certainly when your application is running at a customer's office, and you don't have Delphi there.

procedure TForm1.ClearListBoxItems;
var
 I: Integer;
begin
 with ListBox1.Items do
   for I := 0 to Count - 1 do
     if assigned(Objects[I]) then Objects[I].Free;
end;
0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6485675
Hi duti,

TObject.Free checks for nil, therefore your check

if Assigned(Objects[I]) then ...

is not necessary.
0
 
LVL 1

Expert Comment

by:malsoft
ID: 6485752
duti,

Admittedly, "eating exceptions" is a BAD THING generally, but there are valid reasons for doing so in this instance. Plus, if you wanted to, you could add proper catching of the error(s) you might expect and re-raise the unhandled ones as needs be...

However, in this occasion we _do_ need to eat the exceptions. The loop in which the freeing is done will continue to work even if one object has already been Free'd, but the pointer referencing it is non-nil (which is normal for a Free operation). In those instances, Free'ing an already Free'd object will result in an EAccessViolation error and display an "error dialog". The same would also happen for the Assigned(Objects[I]) operation as well. If the object is Free'd, but the pointer is not nil'd afterwards, the condition will be fulfilled and you'll get an EAccessViolation error again.
0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6485788
Hi malsoft,

I haven't looked at your code until now ;-)

> The loop in which the freeing is done
will continue to work

Which is IMHO wrong. The exception should not be suppressed.

> The loop in which the freeing is done
will continue to work even if one object has already been Free'd, but the pointer referencing it is
non-nil (which is normal for a Free operation).

If there is code that Frees objects referenced by the listbox items (which would be poor design, IMHO) then additional work is required to provide notification to the listbox to remove the item and reference.
Keeping invalid references doesn't make sense and is dangerous. So is your exception suppressing mechanism.

Just my 2c.
TOndrej
0
 
LVL 1

Expert Comment

by:malsoft
ID: 6485897
Hi TOndrej,

I said
> The loop in which the freeing is done
will continue to work

You said
>Which is IMHO wrong. The exception should not be suppressed.

However, you missed my point that you _can_ add whatever exception handling you want in there if you wanted to:

eg
  try
    // ...
  except
    on EAccessViolation do begin
      // handle already Free'd instances here
      end;
    on EException do
      // Everything else gets raised again
      raise;
  end;

As this code will be (normally) called within the context of the OnDestroy event, we need to ensure that the destruction completes, otherwise we end up with a memory leak (the remainder of the list will be skipped, leaving objects floating around in memory limbo).

You then said:
> If there is code that Frees objects referenced by the listbox items (which would be poor design, IMHO) then additional work is required to provide notification to the listbox to remove the item and reference.

This I would agree with, but as we do not know the nature of the objects that will be added to the list, some assumptions must be made. Here's an example for you: Say the objects added to the list were actual controls, created dynamically. To get them to display correctly, you need to assign them a Parent. When this Parent is notified of the Form's destruction, any child-control will also be destroyed at the same time (you don't call the Free method, the Parent tells all children to destroy themselves). When you come to do your own clean-up, you may try to Free the control you dynamically created and you will then get an EAccessViolation error because the object is already destroyed, but the reference to it is non-nil, hence Assigned() would still work and Free would attempt to re-Free the memory as it is not a nil value.

You said:
> Keeping invalid references doesn't make sense and is dangerous. So is your exception suppressing mechanism.

Keeping invalid references, I would whole-heartedly agree with. However, taking the above example into account, there's not much you can do about it. You either let the loop complete, freeing ALL objects, or you risk leaving some of the objects "floating around" inside the applications memory space when the unhandled exception jumps you out of the loop. In those occasions, the exception suppression is an asset rather than a liability.

I've not got any pennies to hand, but I've got a nickel! (that's inflation for you), so the tally stands at: 200pts and 7cents ;-)
0
 

Expert Comment

by:duti
ID: 6486020
Hi malsoft,

you got a point there, that the loop should continue. Even when one object could not be freed, the rest should at least be tried.  But using the 'assigned'-test, you can skip the exception-stuff and so make the code a little bit more readable. (if you are certain that uninitialized pointers will be nil)
And doing so, we use lesser keystrokes ;-)



Hi TOndrej,

TObject.Free might check if it is nil, the code never get that far, because 'Free' is a method of an object, and if the object itself is nil, the code will do an EAccessViolation before calling 'Free'.  I believe the procedure SysUtils.FreeAndNil would be more appropriate. (so it's shorter, readable, and saves even more keystrokes.)

But unfortunately the code
  FreeAndNil(ListBox1.items.Objects[1]);
won't compile. :(  
("Constant object cannot be passed as var parameter")

0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6486716
duti,

> TObject.Free might check if it is nil, the code never
> get that far, because .... <snip>

try it:

var
  Obj: TObject;
begin
  Obj := nil;
  Obj.Free;
end;

> when one object could not be freed, the rest
should at least be tried
I disagree as I explained above.

malsoft,
> Parent tells all children to destroy themselves

and you need to get a notification of this so you can update your references...

> When you come to do your own clean-up, you may try
> to Free the control you dynamically created and you
> will then get an EAccessViolation error because the
> object is already destroyed

...exactly, to make sure this thing never happens.

> you risk leaving some of the objects "floating around" inside the applications memory space

Agreed. It seems to be a matter of taste. I prefer seeing all error messages. If there is an error in freeing an object, I want to know about it. I try to take enough precautions to never release code that would raise errors in object destruction; however if I do, I want the user to see it and hopefully report it, so I can fix it.

Cheers for an interesting discussion, and
Have a nice Delphi day :-)
TOndrej
0
 

Author Comment

by:NicoleKabitzki
ID: 6486744
Ok, that works! Thanx for your answer.

Bye
  Nicole
0

Featured Post

Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
how to center only a line in richedit? 4 61
delphi parse string to params 3 136
JAudiorecorder record freezing the app 29 74
Dynamically Created Query 3 62
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question