Discussion:
TList
(too old to reply)
Stark
2011-11-13 18:17:40 UTC
Permalink
A frame containing a row of edits is represented by a TFrameEdit class
(derived from a Frame class).
As you can see in the following code, a button let the user add additional
row of edits to a pane. Each new newRow object created, is also added to a
TList:

procedure TForm1.BtnAddRowClick(Sender: TObject);
var
newRow: TFrameEdit;
begin
newRow := TFrameEdit.Create(Self); //creating a new instance of TFrame
Edit
inc(numRows);
newRow.Top := lastRow.Top + lastRow.Height;
newRow.Left := lastRow.Left;
newRow.Name := 'Row' + inttostr(numRows);
newRow.Parent := Self;
lastRow := newRow;

frmList.Add(newRow); // adding object to TList frmList
end;

I also need to let the user remove the rows he created on the panel. I am
trying to use the following simple proc, but it doesn't work:

procedure TForm1.BtnClearRowClick(Sender: TObject);
var i: integer;
aFrame: TFrameEdit;
begin
for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[I]);
if aFrame <> nil then
FreeAndNil(aFrame);
end;

Suppose 3 rows where created, then the aFrame representing Row3 is cleared
all right. The attempt to clear Row2 fails with "Invalid pointer operation".
In fact, the proc is trying to free again the first object in the TList wich
is Row3. I don't understand why this is not found nil in the "if aFrame <>
nil" test, which would avoid the FreAndNil execution where it fails.
Theri is something conceptual which I miss. Can anyone help ?
Jamie
2011-11-13 18:41:33 UTC
Permalink
Post by Stark
A frame containing a row of edits is represented by a TFrameEdit class
(derived from a Frame class).
As you can see in the following code, a button let the user add
additional row of edits to a pane. Each new newRow object created, is
procedure TForm1.BtnAddRowClick(Sender: TObject);
var
newRow: TFrameEdit;
begin
newRow := TFrameEdit.Create(Self); //creating a new instance of TFrame
Edit
inc(numRows);
newRow.Top := lastRow.Top + lastRow.Height;
newRow.Left := lastRow.Left;
newRow.Name := 'Row' + inttostr(numRows);
newRow.Parent := Self;
lastRow := newRow;
frmList.Add(newRow); // adding object to TList frmList
end;
I also need to let the user remove the rows he created on the panel. I
procedure TForm1.BtnClearRowClick(Sender: TObject);
var i: integer;
aFrame: TFrameEdit;
begin
for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[I]);
if aFrame <> nil then
FreeAndNil(aFrame);
end;
Suppose 3 rows where created, then the aFrame representing Row3 is
cleared all right. The attempt to clear Row2 fails with "Invalid pointer
operation". In fact, the proc is trying to free again the first object
in the TList wich is Row3. I don't understand why this is not found nil
in the "if aFrame <> nil" test, which would avoid the FreAndNil
execution where it fails.
Theri is something conceptual which I miss. Can anyone help ?
it looks like you are not cleaning up the TLIST occupied space.
so at some point, you'll have wild pointers there.
you are Freeing and Nilling only your local object and not that of
which is actually in the Tlist. Let me put it this way, you are freeing
the object and there by calling its destructor how ever, the pointer
still remains in the Tlist and the next time you call that, you'll have
problems because now the point isn't Nill but it isn't looking at a
valid object anymore too!

do this..

FreeAndNil(TFrameEdit(frmList.Items[?]));
frmlist.delete[?]

Jamie
Stark
2011-11-14 17:09:05 UTC
Permalink
Post by Jamie
Post by Stark
A frame containing a row of edits is represented by a TFrameEdit class
(derived from a Frame class).
As you can see in the following code, a button let the user add
additional row of edits to a pane. Each new newRow object created, is
procedure TForm1.BtnAddRowClick(Sender: TObject);
var
newRow: TFrameEdit;
begin
newRow := TFrameEdit.Create(Self); //creating a new instance of TFrame
Edit
inc(numRows);
newRow.Top := lastRow.Top + lastRow.Height;
newRow.Left := lastRow.Left;
newRow.Name := 'Row' + inttostr(numRows);
newRow.Parent := Self;
lastRow := newRow;
frmList.Add(newRow); // adding object to TList frmList
end;
I also need to let the user remove the rows he created on the panel. I am
procedure TForm1.BtnClearRowClick(Sender: TObject);
var i: integer;
aFrame: TFrameEdit;
begin
for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[I]);
if aFrame <> nil then
FreeAndNil(aFrame);
end;
Suppose 3 rows where created, then the aFrame representing Row3 is
cleared all right. The attempt to clear Row2 fails with "Invalid pointer
operation". In fact, the proc is trying to free again the first object in
the TList wich is Row3. I don't understand why this is not found nil in
the "if aFrame <> nil" test, which would avoid the FreAndNil execution
where it fails.
Theri is something conceptual which I miss. Can anyone help ?
it looks like you are not cleaning up the TLIST occupied space.
so at some point, you'll have wild pointers there.
you are Freeing and Nilling only your local object and not that of
which is actually in the Tlist. Let me put it this way, you are freeing
the object and there by calling its destructor how ever, the pointer still
remains in the Tlist and the next time you call that, you'll have
problems because now the point isn't Nill but it isn't looking at a valid
object anymore too!
do this..
FreeAndNil(TFrameEdit(frmList.Items[?]));
frmlist.delete[?]
Jamie
Yes, it works ! I had to put it as followos:

for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[i]);
FreeAndNil(aFrame);
frmList.Delete(i);
break;
end;

The expression FreeAndNil(TFrameEdit(frmList.Items[i])); is not accepted.
But is the statement "frmList.Delete(i);" deleting an obiect or a
reference to the object ? I understand that adding to the TList you are
really storing other pointers to the object created. Isn'it so ?
Jamie
2011-11-14 22:29:50 UTC
Permalink
Post by Stark
Post by Jamie
Post by Stark
A frame containing a row of edits is represented by a TFrameEdit
class (derived from a Frame class).
As you can see in the following code, a button let the user add
additional row of edits to a pane. Each new newRow object created, is
procedure TForm1.BtnAddRowClick(Sender: TObject);
var
newRow: TFrameEdit;
begin
newRow := TFrameEdit.Create(Self); //creating a new instance of
TFrame Edit
inc(numRows);
newRow.Top := lastRow.Top + lastRow.Height;
newRow.Left := lastRow.Left;
newRow.Name := 'Row' + inttostr(numRows);
newRow.Parent := Self;
lastRow := newRow;
frmList.Add(newRow); // adding object to TList frmList
end;
I also need to let the user remove the rows he created on the panel.
procedure TForm1.BtnClearRowClick(Sender: TObject);
var i: integer;
aFrame: TFrameEdit;
begin
for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[I]);
if aFrame <> nil then
FreeAndNil(aFrame);
end;
Suppose 3 rows where created, then the aFrame representing Row3 is
cleared all right. The attempt to clear Row2 fails with "Invalid
pointer operation". In fact, the proc is trying to free again the
first object in the TList wich is Row3. I don't understand why this
is not found nil in the "if aFrame <> nil" test, which would avoid
the FreAndNil execution where it fails.
Theri is something conceptual which I miss. Can anyone help ?
it looks like you are not cleaning up the TLIST occupied space.
so at some point, you'll have wild pointers there.
you are Freeing and Nilling only your local object and not that of
which is actually in the Tlist. Let me put it this way, you are freeing
the object and there by calling its destructor how ever, the pointer
still remains in the Tlist and the next time you call that, you'll have
problems because now the point isn't Nill but it isn't looking at a
valid object anymore too!
do this..
FreeAndNil(TFrameEdit(frmList.Items[?]));
frmlist.delete[?]
Jamie
for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[i]);
FreeAndNil(aFrame);
frmList.Delete(i);
break;
end;
The expression FreeAndNil(TFrameEdit(frmList.Items[i])); is not accepted.
But is the statement "frmList.Delete(i);" deleting an obiect or a
reference to the object ? I understand that adding to the TList you are
really storing other pointers to the object created. Isn'it so ?
Delphi must be doing some extra type checking, that works in the older
versions.

You could always try this FreeAndNill(frmList.Item[i] as TFramEdit);

But in any case, you have it working also, you do need to remove the
entry from the list because it'll be there next time containing a
pointer that isn't Nill and will point somewhere wildly.

I guess you could always mark that entry location as NIL but then
you'd have to manage that other wise, you'll just keep increasing the
size of the Tlist with dead space in it.

Just think of Tlist as a managed array of 32 bit place holders..
which is all it is. You can do the same thing if you simply had an array
of TframeEdit pointers somewhere, only the TList has the build in code to
manage the entries in case you want to delete, insert, clear, add too etc.


Jamie
Stark
2011-11-15 11:11:01 UTC
Permalink
Post by Jamie
Delphi must be doing some extra type checking, that works in the older
versions.
You could always try this FreeAndNill(frmList.Item[i] as TFramEdit);
But in any case, you have it working also, you do need to remove the
entry from the list because it'll be there next time containing a pointer
that isn't Nill and will point somewhere wildly.
I guess you could always mark that entry location as NIL but then
you'd have to manage that other wise, you'll just keep increasing the size
of the Tlist with dead space in it.
Just think of Tlist as a managed array of 32 bit place holders.. which
is all it is. You can do the same thing if you simply had an array of
TframeEdit pointers somewhere, only the TList has the build in code to
manage the entries in case you want to delete, insert, clear, add too etc.
Jamie
Thanks Jamie. The expression FreeAndNill(frmList.Item[i] as TFramEdit);
doesn't work either. However, my proc works and looks as follows:

procedure TForm1.BtnClearAllRowsClick(Sender: TObject);
var
i: integer;
begin
for i := frmList.Count - 1 downto 0 do
begin
aFrame := TFrameEdit(frmList.Items[i]);
FreeAndNil(aFrame);
frmList.Delete(i);
end;
end;
I think this does free all of the TFrameEdit objects previously created (see
my first message).
Since the frmList held pointers to those objects, does deleting the TList
entries get rid of the object references also ?
Maarten Wiltink
2011-11-16 23:07:30 UTC
Permalink
Post by Jamie
Post by Stark
Post by Jamie
do this..
FreeAndNil(TFrameEdit(frmList.Items[?]));
aFrame := TFrameEdit(frmList.Items[i]);
FreeAndNil(aFrame);
The expression FreeAndNil(TFrameEdit(frmList.Items[i])); is not accepted.
Delphi must be doing some extra type checking, that works in the older
versions.
You could always try this FreeAndNill(frmList.Item[i] as TFramEdit);
Wild goose chase. It has nothing to do with classtypes. FreeAndNil has
a _var_ parameter, and a property value mustn't be used for one.

(It can sometimes be technically possible and may for that reason perhaps
have been allowed where possible in the past, but since it is not always
possible, it should have been forbidden in all cases in the first place.)

Groetjes,
Maarten Wiltink

Loading...