The pointer to the TListview items' data property seems to work fine. The
correct filename or folder is selected on the server when the listview item
is selected.
// The record pointed to by each TListview items' data property
prItemData = ^rItemData;
rItemData = record
integer: integer;
end;
When I fill the List view I call:
List Item := ListView1.Items.Add;
with List Item do
begin
Data := New( prItemData );
{Set the items remote index}
prItemData( Data )^.integer := I;
end;
This too seems to work.
My question is how do I dispose of the pointer when the app closes to
prevent memory leaks? I do not have much experience using pointers.
Regards,
Bill
> // The record pointed to by each TListview items' data property
> prItemData = ^rItemData;
> rItemData = record
> integer: integer;
> end;
You don't need a record at all when you are storing just a single integer.
You can store the integer value itself directly into the Data property.
Simply typecast it when needed, ie:
ListItem := ListView1.Items.Add;
ListItem.Data := TObject(I);
//...
Index := Integer(ListItem.Data);
> My question is how do I dispose of the pointer when the
> app closes to prevent memory leaks?
If you continue to use a record, then use the ListView's OnDeletion event to
free it when the TListItem itself is being destroyed, ie:
procedure TForm1.ListView1Deletion(Sender: TObject: Item: TListItem);
begin
if Item.Data <> nil then
begin
FreeMem(Item.Data);
Item.Data := nil;
end;
end;
With that said, an alternative approach would be to derive a new class from
TListItem instead and make the value be a new member of that class. Then
you don't have to worry about freeing anything yourself at all. For
example:
type
TFTPListItem = class(TListItem)
public
FTPIndex: Integer;
end;
ListItem := ListView1.Items.Add;
TFTPListItem(ListItem).FTPIndex := I;
If you are using D6 or higher, TListView has an OnCreateItemClass event that
you can use to provide the new class for each item:
procedure TForm1.ListView1CreateItemClass(Sender: TCustomListView; var
ItemClass: TListItemClass);
begin
ItemClass := TFTPListItem;
end;
For you are using D5 or earlier, however, then you have to derive a new
component from TListView and override the virtual CreateListItem() method
directly instead, ie:
type
TFTPListView = class(TListView)
protected
function CreateListItem: TListItem; override;
end;
function TFTPListView.CreateListItem: TListItem;
begin
Result := TFTPListItem.Create(Items);
end;
Gambit
For now I choose to just store the single integer value. I added
ReportMemoryleaksOnShutdown := true; to FormCreate in Delphi 2007 patch 1
and I get "The unexpected small block leaks are 13-20 bytes: String X 1"
What is String X 1? Any suggestion on how to track this down? Using Synapse
and DeveloperExpress components along with a few other components.
Regards,
Bill
Bill
> What is String X 1?
It is telling you that 1 instance of a "String" object was leaked.
> Any suggestion on how to track this down?
You would have to replace the memory manager with the full FastMM from
http://fastmm.sourceforget.net in order to enable full tracing of leaks.
Otherwise, you will have to use an external profiler/checker instead.
Gambit
> Isn't the Listview.Data destroyed along with the TListView?
No. The Data property is just a raw pointer. It has no concept of what
kind of memory (if any) was assigned to it. So it can't free it
automatically even if it wanted to.
Gambit
procedure TFrmMain.FormDestroy( Sender: TObject );
var
i: integer;
ListItem: TListitem;
begin
for i := 0 to ListView1.Items.Count - 1 do begin
ListItem := ListView1.Items.Item[i];
Dispose( ListItem.Data );
end;
end;
Thanks,
Bill
> So it is best to call Dispose like this
No, that is not best. Use the OnDeletion event like I showed you earlier,
and only if you are actually allocating memory to begin with. If you store
the integer value using a typecast instead, there is is no allocated memory
to free.
Gambit