Delphi component with a variable amount of TPictures - delphi

I'm trying to create a component descending from TImage, with the difference that I can assign a variable number of TPictures in the property list (not assign the TPictures by code) and activate one of them by code to be displayed in the TImage.
It would not be a problem to have a property that sets the overall number of TPictures (length of the dynamic array), if that's necessary to have all the TPictures assignable within the properties.
unit ImageMultiStates;
interface
uses
Vcl.Graphics, Vcl.StdCtrls, System.SysUtils, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Forms;
type
TPictures = Array of TPicture;
TImageMultiStates = class(TImage)
private
FPictures: TPictures;
procedure SetPicture(Which: Integer; APicture: TPicture);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Activate(Which: Integer);
published
property Images: TPictures read FPictures write FPictures; default;
end;
procedure Register;
implementation
constructor TImageMultiStates.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
for TPicture in FPictures do
TPicture := TPicture.Create;
end;
destructor TImageMultiStates.Destroy;
var
APicture: TPicture;
begin
for APicture in FPictures do
APicture.Free;
inherited Destroy;
end;
procedure TImageMultiStates.Activate(Which: Integer);
begin
Picture.Assign(FPictures[Which]);
end;
procedure TImageMultiStates.SetPicture(Which: Integer; APicture: TPicture);
begin // i would also like to use SetPicture instead of "write FPictures"
FPictures[Which].Assign(APicture);
if Which=0 then // because: First Picture will be displayed in the VCL editor
Picture.Assign(FPictures[Which]);
end;
procedure Register;
begin
RegisterComponents('Standard', [TImageMultiStates]);
end;
end.
I turned this code around in many ways, but i just can't get anything to work really.
I do have this exact same idea already working in my component 'Image2States'. Then I needed 'Image4States' and so on, until I decided that I absolutely need this with a variable amount of TPictures...

You told us that your approach "didn't work" (I assume you meant "didn't compile"): That is because of a few syntactical errors and because you are not using the right tool for the job.
If you only have pictures of the same size, then think about using the already existing, well tested and IDE-supported TImageList and don't try to reinvent the wheel.
Try...
If, however, you must have a list of pictures of different sizes, then use a TObjectList and not an array of TPicture. TObjectLists allow you to add, remove, query, etc. objects and they can automatically free them, if you wish.
If your compiler supports generics, then include System.Generics.Collections and use a TObjectList<TPicture> to manage your pictures. That way, you don't have to cast to TPicture, because the generics lists are type safe.
If it doesn't support them, include the unit Contnrs and use a TObjectList. When reading from that list, you will have to cast using as, i.e. as TPicture, but otherwise you can do similar things.
Finally...
The name of your type makes me think you only need multiple states for a certain control. In that case, I think a TImageList is the best tool for the job (and it already is for other controls with similar needs) and there is no need to make your own one. But if you want to make your own, don't use a dynamic array and don't produce loops like the one with for TPicture in FPictures do. Do yourself a favour and use an object list.
End

I would answer this by asking: What are you expecting this to do:
for TPicture in FPictures do
TPicture := TPicture.Create;
Firstly, as written, this simply does not compile. The TPicture loop variable is not declared (and being the same as the name of a type would cause subsequent compilation errors even if it were declared).
Even assuming that the loop were valid, compilable code, when the constructor executes FPictures is an empty array so this loop will execute 0 (zero) times. You do not show any code which tells the component how many pictures it should support so it always supports 0 (zero).
The problems don't end there.
If you did declare an appropriate variable for use as the loop variable, the loop code will still not compile since it involves an assignment to the loop variable which is not permitted.
This is true even in simple for-loops as well as iterator based loops such as the one you were attempting to use. In the case of simple loops, this is to enable the compiler to produce optimal code. In the case of iterator loops it also protects you from mistakes that might otherwise be easy to make.
Consider that in this case on each iteration of the loop, you would create a new TPicture instance and assign it to the loop variable but this does not assign it to the item in the array from which the loop variable is initialised.
Perhaps the easiest way to explain this is to "unroll the loop" (Unrolling a loop is re-writing the code explicitly for each iteration, as if there were no loop at all).
So consider if the loop contained 2 items, and let's also change the name of the loop variable to make things both valid as well as a bit clearer. We'll use simply pic. In other words we'll unroll a 2 iteration version of this loop:
for pic in FPictures do // fPictures contains 2 items
pic := TPicture.Create;
Remember, this does not compile and the reason why this helps prevent us making mistakes becomes apparent when we unroll the loop that it would create, if it were possible:
// Iteration #1
pic := fPictures[0];
pic := TPicture.Create;
// Iteration #2
pic := fPictures[1];
pic := TPicture.Create;
Hopefully you see the problem. You overwrite the value of the loop variable on each iteration, but this does not modify the array items themselves. Worse than that, as a result, each iteration of the loop is leaking a TPicture.
Hopefully this should now help you understand why your code isn't working (actually, can't work) and make the necessary corrections.
You need some mechanism to set the number of pictures supported by your component
You need to correctly initialise the items in the array holding those pictures

Related

Delphi release interface pointer

I'm struggling with interfaces in Delphi. This question might be trivial, but I am new to Delphi, so please excuse.
I have a TreeView with customized nodes, which hold an interface to an object (essentially, just like it is proposed here: Storing interface pointer inside tree view nodes).
The problem is, once I delete a node (in order to redraw the treeview) and set the interface variable to nil (freeing won't do with interfaces for some reason I haven't fully understood), the weirdest thing happens:
In my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.
I can't explain this. Does anybody know a workaround, or the possible reason for this behavior? BTW, I am using Delphi 10.2 Tokyo.
Here's my quite unspectacular destroy method:
myNode.destroy;
begin
intf:= nil;// intf holds the interface to the object
end;
Edit: this is a simplified version of my code:
The object I'm referring to: (I have several similar classes which look like obj but are slightly different and I don't know which one will be stored in the interface, but all share these variables)
Obj = class(InterfacedObject, IMyinterface)
count: integer; //this remains the same
children: array of ChildObj; //this will be emptied
name: string; //this will be set to ''
procedure addChild;
procedure IMyInterface.add = addChild;
end;
My customized treeNode:
MyNode = class(TTreeNode)
Intf: IMyinterface;
destructor destroy; override;
end;
Inside my class manages the TreeView:
MyForm.ReloadTree;
begin
if myTreeView.Items.Count > 0 then
begin
myTreeView.Items.Clear;
end
for I:= 0 to RootObj.Count-1 do
begin
myTreeView.Items.AddChild(MyTreeview.Items[0], RootObj.Children[i].name);
(myTreeView.Items[0][i] as MyNode).Intf := Intf(RootObj.Children[i]);
//I will proceed iterating over all children and their children, doing
//the same process, a level higher in the treeView
//...
end;
end;
in my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.
This is perfectly normal behavior. Strings and interfaces are compiler-managed types. Integers are not. When an object is destructed, compiler-managed data members are automatically deallocated as needed, which in the case of strings and interfaces involves nil'ing the pointers to their referenced data. The containing object itself is not zeroed out completely, so non-managed types, like integers, are not overwritten in memory.
Now, that being said, I see some bugs in your ReloadTree() procedure.
Your for loop is exceeding the upper bound of the RootObj.Children[] list.
When calling AddChild(), the second parameter is a string. You are passing RootObj.Children[i] in that parameter. But, in the next statement, you are type-casting the same RootObj.Children[i] value to an interface when assigning the MyNode.Intf field. A string is not an interface. So, what exactly does RootObj.Children[] contain - strings or interfaces?
When assigning the MyNode.Intf field, you are always accessing the first node in the TreeView, instead of the newly added node.

How to replace TListbox Items property with my own published object list based type in a TCustomListBox control?

Overview
This question is a second attempt based on this one I recently asked: How can I make a TList property from my custom control streamable?
Although I accepted the answer in that question and it worked, I soon realized that TCollection is not the solution or requirement I was looking for.
Requirements
To keep my requirements as simple and clear to understand as possible, this is what I am attempting to:
Derive a new custom control based on TCustomListBox
Replace the Items property with my own Items type, eg a TList.
The TList (Items property) will hold objects, each containing a caption and a image index property etc.
Ownerdraw my listbox and draw its icons and text etc.
Create a property editor to edit the Items at design-time.
With that in mind, I know how to create the custom control, I know how to work with TList or even TObjectList for example, I know how to ownerdraw the control and I also know how to create the property editor.
Problem
What I don't know is how to replace the standard listbox Items type with my own? well I kind of do (publishing my own property that shares the same name), only I need to make sure it is fully streamable with the dfm.
I have searched extensively on this subject and have tried studying code where TListView and TTreeView etc publishes its Items type but I have found myself more confused than ever.
In fact I came across this very old question asked by someone else on a different website which asks very much what I want to do: Streaming a TList property of a component to a dfm. I have quoted it below in the event the link is lost:
I recently wrote a component that publishes a TList property. I then created a property editor for the TList to enable design-time editing. The problem is that the TList doesn't stream to the dfm file, so all changes are lost when the project is closed. I assume this is because TList inherits from TObject and not from TPersistant. I was hoping there was an easy work around for this situation (or that I have misunderstood the problem to begin with). Right now all I can come up with is to switch to a TCollection or override the DefineProperties method. Is there any other way to get the information in the TList streamed to and from the dfm?
I came across that whilst searching keywords such as DefineProperties() given that this was an alternative option Remy Lebeau briefly touched upon in the previous question linked at the top, it also seemed to be the answer to that question.
Question
I need to know how to replace the Items (TStrings) property of a TCustomListBox derived control with my own Items (TList) or Items (TObjectList) etc type but make it fully streamable with the dfm. I know from previous comments TList is not streamable but I cannot use TStrings like the standard TListBox control does, I need to use my own object based list that is streamable.
I don't want to use TCollection, DefineProperties sounds promising but I don't know how exactly I would implement this?
I would greatly appreciate some help with this please.
Thank you.
Override DefineProperties procedure in your TCustomListBox (let's name it TMyListBox here). In there it's possible to "register" as many fields as you wish, they will be stored in dfm in the same way as other fields, but you won't see them in object inspector. To be honest, I've never encountered having more then one property defined this way, called 'data' or 'strings'.
You can define 'normal' property or binary one. 'Normal' properties are quite handy for strings, integers, enumerations and so on. Here is how items with caption and ImageIndex can be implemented:
TMyListBox = class(TCustomListBox)
private
//other stuff
procedure ReadData(reader: TReader);
procedure WriteData(writer: TWriter);
protected
procedure DefineProperties(filer: TFiler); override;
//other stuff
public
//other stuff
property Items: TList read fItems; //not used for streaming, not shown in object inspector. Strictly for use in code itself. We can make it read-only to avoid memory leak.
published
//some properties
end;
that's DefineProperties implementation:
procedure TMyListBox.DefineProperties(filer: TFiler);
begin
filer.DefineProperty('data', ReadData, WriteData, items.Count>0);
end;
fourth argument, hasData is Boolean. When your component is saved to dfm, DefineProperties is called and it's possible to decide at that moment is there any data worth saving. If not, 'data' property is omitted. In this example, we won't have this property if there is no items present.
If we expect to ever use visual inheritance of this control (for example, create a frame with this listBox with predefined values and then eventually change them when put to form), there is a possibility to check, is value of this property any different than on our ancestor. Filer.Ancestor property is used for it. You can watch how it's done in TStrings:
procedure TStrings.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then
begin
Result := True;
if Filer.Ancestor is TStrings then
Result := not Equals(TStrings(Filer.Ancestor))
end
else Result := Count > 0;
end;
begin
Filer.DefineProperty('Strings', ReadData, WriteData, DoWrite);
end;
This would save a little bit of space (or lots of space if image is stored within) and sure is elegant, but in first implementation it can well be omitted.
Now the code for WriteData and ReadData. Writing is much easier usually and we may begin with it:
procedure TMyListBox.WriteData(writer: TWriter);
var i: Integer;
begin
writer.WriteListBegin; //in text dfm it will be '(' and new line
for i:=0 to items.Count-1 do begin
writer.WriteString(TListBoxItem(items[I]).caption);
writer.WriteInteger(TListBoxItem(items[I]).ImageIndex);
end;
writer.WriteListEnd;
end;
In dfm it will look like this:
object MyListBox1: TMyListBox
data = (
'item1'
-1
'item2'
-1
'item3'
0
'item4'
1)
end
Output from TCollection seems more elegant to me (triangular brackets and then items, one after another), but what we have here would suffice.
Now reading it:
procedure TMyListBox.ReadData(reader: TReader);
var item: TListBoxItem;
begin
reader.ReadListBegin;
while not reader.EndOfList do begin
item:=TListBoxItem.Create;
item.Caption:=reader.ReadString;
item.ImageIndex:=reader.ReadInteger;
items.Add(item); //maybe some other registering needed
end;
reader.ReadListEnd;
end;
That's it. In such a way rather complex structures can be streamed with ease, for example, two-dimensional arrays, we WriteListBegin when writing new row and then when writing new element.
Beware of WriteStr / ReadStr - these are some archaic procedures which exist for backward compatibility, ALWAYS use WriteString / ReadString instead!
Other way to do is to define binary property. That's used mostly for saving images into dfm. Let's say, for example, that listBox has hundreds of items and we'd like to compress data in it to reduce size of executable. Then:
TMyListBox = class(TCustomListBox)
private
//other stuff
procedure LoadFromStream(stream: TStream);
procedure SaveToStream(stream: TStream);
protected
procedure DefineProperties(filer: TFiler); override;
//etc
end;
procedure TMyListBox.DefineProperties(filer: TFiler);
filer.DefineBinaryProperty('data',LoadFromStream,SaveToStream,items.Count>0);
end;
procedure TMyListBox.SaveToStream(stream: TStream);
var gz: TCompressionStream;
i: Integer;
value: Integer;
item: TListBoxItem;
begin
gz:=TCompressionStream.Create(stream);
try
value:=items.Count;
//write number of items at first
gz.Write(value, SizeOf(value));
//properties can't be passed here, only variables
for i:=0 to items.Count-1 do begin
item:=TListBoxItem(items[I]);
value:=Length(item.Caption);
//almost as in good ol' Pascal: length of string and then string itself
gz.Write(value,SizeOf(value));
gz.Write(item.Caption[1], SizeOf(Char)*value); //will work in old Delphi and new (Unicode) ones
value:=item.ImageIndex;
gz.Write(value,SizeOf(value));
end;
finally
gz.free;
end;
end;
procedure TMyListBox.LoadFromStream(stream: TStream);
var gz: TDecompressionStream;
i: Integer;
count: Integer;
value: Integer;
item: TListBoxItem;
begin
gz:=TDecompressionStream.Create(stream);
try
gz.Read(count,SizeOf(count)); //number of items
for i:=0 to count-1 do begin
item:=TListBoxItem.Create;
gz.Read(value, SizeOf(value)); //length of string
SetLength(item.caption,value);
gz.Read(item.caption[1],SizeOf(char)*value); //we got our string
gz.Read(value, SizeOf(value)); //imageIndex
item.ImageIndex:=value;
items.Add(item); //some other initialization may be needed
end;
finally
gz.free;
end;
end;
In dfm it would look like this:
object MyListBox1: TMyListBox1
data = {
789C636260606005E24C86128654865C064386FF40802C62C40002009C5607CA}
end
78 is sort of signature of ZLib, 9C means default compression, so it works (there are only 2 items actually, not hundreds). Of course, this is just one example, with BinaryProperties any possible format may be used, for example saving to JSON and putting it into stream, or XML or something custom. But I'd not recommend to use binary unless it's absolutely inevitable, because it's difficult to see from dfm, what happens in component.
It seems like good idea to me to actively use streaming when implementing component: we can have no designer at all and set all values by manually editing dfm and see if component behaves correctly. Reading/loading itself can be tested easily: if component is loaded, then saved and text is just the same, it's all right. It's so 'transparent' when streaming format is 'human-readable', self-explaining that it often overweighs drawbacks (like file size) if there are any.

Delphi TDictionary : values are replace automatically when have the same key

I am using delphi XE5.
This a resume of my code, Next code works, but there have to be something in my code that destroy normal behaviour:
unit Class1;
type
TClass1 = class
private
FDic:TDictionary<String,String>.Create;
public
constructor create;
procedure insertValue(key,value:String);
end;
implementation
constructor TClass1.create;
begin
FDic:=TDictionary<String,String>.Create;
end;
procedure insertValue(key,value:String);
begin
if(FDic.ContainsKey(key))then
FDic[key] := value
else
begin
FDic.Add(key,value);
end;
end.
And now another unit:
unit Class2;
type
uses Class2;
TClass1 = class
public
class2 :TClass2;
TS: TStringList;
procedure DoSomething;
end;
implementation
procedure TClass1.DoSomething;
var
i: Integer;
c,test: TClass1;
begin
c := TClass1.create;
c.insertValue('height','.34cm');
c.insertValue('width','22cm');
c.insertValue('radio','2cm');
TS.AddObject('square',c);
c := TClass1.create;
c.insertValue('height','.88cm');
c.insertValue('width','11cm');
c.insertValue('top','12cm');
TS.AddObject('circle',c);
test := TS.Objects[0] as TClass1;//test.FDic height should be .34cm but gets the value of the last object create, the same for width an common keys.
//here when I look for the values of FDic test.FDic.Items['height'] the value is .88cm instead of .34cm, each time the values of any element of the Dictionary is replace with the previous of the last object created. And the memory address is the same. Why don't create a new memory address for any new element if it is a different object.
That is a resume of my code, I can put all my code because is too big, but I would like to know where I can search to resolve this problem. I am not is not easy, maybe I am not the only one with that problema, maybe some class in the uses, class variables, there is something that causes a memory problema in that dictionary, but no way to find it.
It's a little hard to be sure of the problem because you posted code that does not compile. For future reference, please don't do that. It's good to cut down the code to a small size, but you should then make it into a small console application that compiles and runs and demonstrates the fault. In spite of this, I think that I can see where the problem is.
You are creating objects and then adding them to a string list with
TS.AddObject(...);
But then you never free those objects. That, I guess, is the source of the leak. You can deal with this by setting the OwnsObjects property of the string list to True.
Specifies whether the string list owns the objects it contains.
The OwnsObjects property specifies whether the string list owns the
stored objects or not. If the OwnsObjects property is set to True,
then the Destroy destructor will free up the memory allocated for
those objects.
That I think will explain the leaks. The other part of your question is why index 0 contains the item that you added second. The only explanation for that, given the code in the question, is that your string list has the Sorted property set to True.
Again, I'm inferring this with a little intuition, but if you have only posted a complete program that compiled and run then your question could have been answered with complete certainty.
I do wonder whether or not a string list is the correct class to be using here. Perhaps you would be better with TObjectDictionary<string, TClass1>. You would create it like this:
dict := TObjectDictionary<string, TClass1>.Create([doOwnsValues]);
The use of doOwnsValues tells the dictionary that it is take assume ownership of the objects that you add as values and destroy them when they are removed from the dictionary, or when the dictionary is destroyed.
Note also that your insertValue method can be implemented in a far simpler way using the AddOrSetValue method.

Access Violation when handling forms

I have procedure to show/hide one element on TForm like that:
procedure ShowHideControl(const ParentForm: TForm; const ControlName: String; ShowControl: Boolean);
var
i: Integer;
begin
for i := 0 to pred(ParentForm.ComponentCount) do
begin
if (ParentForm.Components[i].Name = ControlName) then
begin
if ShowControl then
TControl(ParentForm.Components[i]).Show
else
TControl(ParentForm.Components[i]).Hide;
Break;
end;
end;
end;
then I try to use it like:
procedure TForm1.Button6Click(Sender: TObject);
begin
ShowHideEveryControl(TForm(TForm1), 'Button4', True);
end;
Why do I get Access Violation on Button6 click?
For me everything is OK... Button4 exists as a child :)
This cast is wrong:
TForm(TForm1)
You are telling the compiler to ignore the fact that TForm1 is not a TForm instance, and asking it to pretend that it is. That is fine until you actually try to use it as an instance, and then the error occurs.
You need to pass a real instance to a TForm descendent. You can write it like this:
ShowHideEveryThing(Self, 'Button4', True);
Just in case you are not clear on this, the parameter of your procedure is of type TForm. That means you need to supply an instance of a class that either is, or derives from TForm. I repeat, you must supply an instance. But you supply TForm1 which is a class.
And then the next problem comes here:
if (ParentForm.Components[i].Name = FormName) then
begin
if ShowForm then
TForm(ParentForm.Components[i]).Show
else
TForm(ParentForm.Components[i]).Hide;
Break;
end;
Again you have used an erroneous cast. When the compiler tells you that it a particular object does not have a method, you must listen to it. It's no good telling the compiler to shut up and pretend that an object of one type is really an object of a different type. Your button is categorically not a form, so don't try to cast it to TForm.
It is very hard to know what you are actually trying to do here. When you write:
ShowHideEveryThing(Self, 'Button4', True);
It would seem to me to me more sensible to write:
Button4.Show;
It is not a good idea to refer to controls using their names represented as text. It is much safer and cleaner to refer to them using reference variables. That way you let the compiler do its job and check the type safety of your program.
The names used in your function are suspect:
procedure ShowHideEveryThing(const ParentForm: TForm; const FormName: String;
ShowForm: Boolean);
Let's look at them:
ShowHideEveryThing: but you claim that the function should show/hide one element. That does not tally with the use of everything.
FormName: you actually pass a component name, and then look for components owned by ParentForm that have that name. It seems that FormName is wrong.
ShowForm: again, do you want to control visibility of the form, or a single element?
Clearly you need to step back and be clear on the intent of this function.
As an aside, you should generally never need to write:
if b then
Control.Show
else
Control.Hide;
Instead you can write:
Control.Visible := b;
My number one piece of advice to you though is to stop casting until you understand it properly. Once you understand it properly, design your code if at all possible so that you don't need to cast. If you ever really do need to cast, make sure that your cast is valid, ideally by using a checked cast with the as operator. Or at least testing first with the is operator.
Your code shows all the hallmarks of a classic mistake. The compiler objects to the code that you write. You have learnt from somewhere that casting can be used to suppress these compiler errors and now you apply this technique widely as a means to make your program compile. The problem is that the compiler invariably knows what it is talking about. When it objects, listen to it. If ever you find yourself suppressing a compiler error with a cast, take a step back and think carefully about what you are doing. The compiler is your friend.

How should I free an array of objects in a Delphi 7 destructor?

Suppose my Delphi classes look like this:
interface
type
TMySubInfo = class
public
Name : string;
Date : TDateTime;
Age : Integer;
end;
TMyInfo = class
public
Name : string;
SubInfo : array of TMySubInfo;
destructor Destroy; override;
end;
implementation
destructor TMyInfo.Destroy;
begin
// hmmm..
end;
end.
To properly clean up, what should go in the destructor? Is it enough to do SetLength(SubInfo,0), or do I need to loop through and free each TMySubInfo? Do I need to do anything at all?
You need to loop through and free each created object.
You must know, that declaring a array of TMySubInfo doesn't actually create the objects. You have to create them later on.
I would use a TList instead for a more dynamic approach. You could even use a TObjectList that can free all its items when the list gets freed.
You should free each item, like this
destructor TMyInfo.Destroy;
var
I: Integer;
begin
for I:= Low(SubInfo) to High(SubInfo) do
SubInfo[I].Free;
SetLength(SubInfo, 0);
inherited;
end;
You free the objects the same way you allocated them. If you assigned an element's value by calling the constructor of a class, then free the object referenced by that element.
destructor TMyInfo.Destroy;
var
info: TMySubInfo;
begin
for info in SubInfo do
info.Free;
inherited;
end;
That uses syntax introduced in Delphi 2005. If you have an older version, use an explicit loop-control variable:
var
i: Integer;
begin
for i := 0 to High(SubInfo) do
SubInfo[i].Free;
You don't need to call SetLength at the end. A dynamic-array field like SubInfo gets released automatically when the object is destroyed. It works the same as interface, string, and Variant fields.
Agreed with all the above suggestions, but I want to add an (admittedly somewhat anal) recommendation that you always call the FreeAndNil() procedure in preference to the Free method.
Sooner or later you will accidentally access an object that you have already freed. If you have the habit of FreeAndNil-ing everything, then you get an immediate a/v on the line that contains the problem. If you merely Free'd the object, you will likely get a mysterious and apparently unconnected failure some time later...
This might seem obsessive in the context of a destructor, as here. Ok, it is a bit, but either one does it everywhere or not at all.
It is also anal, but Like to free in the reverse order to the creation, for example:
For I := List.Count-1 downto 0 do
List[I].Free;
I view creation and destruction as parethesis () although it makes litte actual execution diference.
Bri
If you created the objects via constructor calls, you need to make calls to Free to free them. If not, you don't.
For every new there should be a free.
Can you not use Finalize?

Resources