Solved

Free ListBox-Objects before closing the application

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

Expert Comment

by:mnasman
Comment Utility
what do u want exactly?
0
 
LVL 8

Accepted Solution

by:
TOndrej earned 200 total points
Comment Utility
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
Comment Utility
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
 

Author Comment

by:NicoleKabitzki
Comment Utility
0
 

Author Comment

by:NicoleKabitzki
Comment Utility
0
 

Expert Comment

by:duti
Comment Utility
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
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Ok, that works! Thanx for your answer.

Bye
  Nicole
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

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…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

762 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

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now