Delphi tstringlist.free erases result [duplicate] - delphi

This question already has answers here:
How do i return an object from a function in Delphi without causing Access Violation?
(10 answers)
Closed last month.
Ok, this I don't understand.
path:=tstringlist.create;
//load up the path with stuff
Result := path;
path.free;
exit;
I would have thought that result would actually equal the path but it apparently doesn't. If I remove path.free from the code above, the result works as it should and it = the path tstringlist but I get a memory leak. When I put path.free in there, the result becomes empty. I understand the memory leak but how is result getting erased if I free the path AFTER I make it := ????
And yes, the above code is inside multiple for loops which is why I'm using exit. I've tried break and try finally and had no luck making those work either.

Let me rephrase your variable and class names and add a few comments:
function MyNewHouse(): TStringList;
var
NewAddress: TStringList;
begin
// Construct a house with walls, windows, doors and a roof. Those
// are the properties and methods that we're able to use later.
NewAddress := House.Create();
// ...fill the house with content, using our walls, windows, doors...
// Only copy the new house's address, not the house in its entirety.
// And certainly not its content.
Result := NewAddress;
// Demolish/Tear down the house, which can only be made once. When
// the house is demolished, you can neither access it, nor tear it
// down anew. However, the address is still somewhat "valid". While
// everything but the spot where it once existed is gone.
NewAddress.Free();
Exit;
end;
Whenever you assign variables of a class type (such as TObject or TStringList or TForm) you're merely copying its address, not its entire content. For copying (believe it or not) the method .Assign() exists:
// Instead of only "Result := NewAddress;"
Result.Assign( NewAddress );
That copies its whole content. This method exists for many classes, and for each different class "copying its content" can mean different things, just like you may want to copy a TStringList's items, but not necessarily its other settings. But if you wanted it that way you would have used Result.Items := NewAddress.Items already in your example...

The reason why Result becomes empty when you include path.free is because Result is just a reference to path. When you call path.free, you are freeing the memory that path occupies, which makes the reference to that memory invalid. When you try to access Result after freeing path, you are trying to access invalid memory, which can result in undefined behavior.
You need to free the returned TStringList outside of the function, you should modify the function as follows:
function getPath: TStringList;
begin
Result := tstringlist.create;
//load up the path with stuff
end;
// usage:
var
path: TStringList;
begin
path := getPath;
try
// use path here
finally
path.Free;
end;
end;
This way, the returned TStringList is created inside the function and is passed as a reference to the caller. The caller is responsible for freeing the TStringList when it is no longer needed by calling Free on it. This is called "resource acquisition is initialization" (RAII) and is a common pattern in Delphi for managing resources such as dynamically allocated objects.
By using this pattern, you can ensure that the TStringList is always properly freed and avoid potential memory leaks.
More advanced trick (XE2+):
type
IScope<T: class> = interface
private
function GetIt: T;
public
property It: T read GetIt;
end;
TScope<T: class> = class(TInterfacedObject, IScope<T>)
private
FValue: T;
public
constructor Create(const AValue: T);
destructor Destroy; override;
function GetIt: T;
end;
constructor TScope<T>.Create(const AValue: T);
begin
inherited Create;
FValue := AValue;
end;
destructor TScope<T>.Destroy;
begin
FValue.Free;
inherited;
end;
function TScope<T>.GetIt: T;
begin
Result := FValue;
end;
function getPath: IScope<TStringList>;
var
path: TStringList;
begin
path := tstringlist.create;
//load up the path with stuff
Result := TScope<TStringList>.Create(path);
end;
// usage:
var
path: TStringList;
begin
path := getPath.It;
// use path here
end; // auto-free here

Related

Is there memory leak here?

is this piece of code safe from memory leaks?
s := TStringList.Create; // create first object
try
// Here line comes that seems to be dangerous
s := GetSomeSettings; // Overrides reference to first object by second one
finally
s.free; // Destroying only second object, leave first object to live somewhere in memory
end;
function GetSomeSettings : TStringList;
var
rawString : string;
settings : TStringList;
begin
// Singleton pattern implementation
// Trying to find already existing settings in class variable
settings := TSettingsClass.fSettings;
// If there is no already defined settings then get them
if not Assigned(settings) then
begin
GetSettingsInDB(rawString);
TSettingsClass.fSettings := ParseSettingsString(rawString);
settings := TSettingsClass.fSettings;
end;
Result := settings;
end;
I'm wondering s := GetSomeSettings; potentially harmful and ignoring first object, keeps it in the memory?
Yes, the StringList created on line 1 is leaked.
Essentialy, you are doing:
s := TStringList.Create;
s := AnotherStringList;
AnotherStringList.Free;
As for the GetSomeSettings routine:
Normally it is not wise or encouraged to return newly created instances as function results, because you transfer the responsibility for ownership and destruction to the calling code. Unless you have a mechanism/framework in place that takes care of it, which seems to be the case with your TSettingsClass, but there is not enough evidence for that in this little piece of code.
Nevertheless, the combination of both pieces of code display another problem: After s.Free, TSettingsClass.fSettings is destroyed but not nil. Thus the second time GetSomeSettings is called, it returns a dangling pointer.
1) you should not ask when you can check in two minutes!
program {$AppType Console};
uses Classes, SysUtils;
type TCheckedSL = class(TStringList)
public
procedure BeforeDestruction; override;
procedure AfterConstruction; override;
end;
procedure TCheckedSL.BeforeDestruction;
begin
inherited;
WriteLn('List ',IntToHex(Self,8), ' going to be safely destroyed.');
end;
procedure TCheckedSL.AfterConstruction;
begin
WriteLn('List ',IntToHex(Self,8), ' was created - check whether it is has matched destruction.');
inherited;
end;
procedure DoTest; var s: TStrings;
function GetSomeSettings: TStrings;
begin Result := TCheckedSL.Create end;
begin
Writeln('Entered DoTest procedure');
s := TCheckedSL.Create; // create first object
try
// Here line comes that seems to be dangerous
s := GetSomeSettings; // Overrides reference to first object by second one
finally
s.free; // Destroying only second object, leave first object
end;
Writeln('Leaving DoTest procedure');
end;
BEGIN
DoTest;
Writeln;
Writeln('Check output and press Enter when done');
ReadLn;
END.
2) Still that could be safe in few niche cases.
in FPC (http://FreePascal.org) S could be a "global property" of some unit, having a setter which would free old list.
in Delphi Classic S could be of some interface type, supported by the created object. Granted, standard TStringList lacks any interface, but some libraries ( for example http://jcl.sf.net ) do offer interface-based string lists, with richer API (iJclStringList type and related).
in Delphi/LLVM all objects were made reference-counted, like interfaces without GUID's. So that code would be safe there.
You can declare S as a record - a so-called Extended Record having re-defined class operator Implicit so that the typecast s{record} := TStringList.Create would free the previous instance before assigning a new one. That is dangerous though, as it is VERY fragile and easy to misuse, and destroy the list in some other place leaving a dangling pointer inside the S record.
Your object may be not that vanilla TStringList, but some subclass, overriding constructors or AfterConstruction to register itself in some list, that would be all-at-once in some place. Kind of Mark/Sweep heap management around large chunk of workload. VCL TComponent may be seen as following this pattern: form is owning its component and frees them when needed. And this is what you - in reduced form - are trying to do with TSettingsClass.fSettings containter (any reference is 1-sized container). However those frameworks do require a loopback: when the object is freed it should also remove itself from all the containers, referencing it.
.
procedure TCheckedSL.BeforeDestruction;
begin
if Self = TSettingsClass.fSettings then TSettingsClass.fSettings := nil;
inherited;
end;
class procedure TSettingsClass.SetFSettings(Value);
var fSet2: TObject;
begin
if fSettings <> nil then begin
fSet2 := fSettings;
f_fSettings := nil; // breaking the loop-chain
fSet2.Destroy;
end;
f_fSettings := Value;
end;
class destructor TSettingsClass.Destroy;
begin
fSettings := nil;
end;
However then - by the obvious need to keep design symmetric - the registration should also be done as a part of the class. Who is responsible for de-registration is usually the one responsible for registration as well, unless there are reasons to skew the design.
procedure TCheckedSL.AfterConstruction;
begin
inherited;
TSettingsClass.fSettings := Self;
end;
...
if not Assigned(settings) then
begin
GetSettingsInDB(rawString);
TCheckedSL.Create.Text := ParseSettingsString(rawString);
settings := TSettingsClass.fSettings;
Assert( Assigned(settings), 'wrong class used for DB settings' );
end;
Result := settings;

With a class operator is an implicit typecast to itself allowed?

I have a record that looks like:
TBigint = record
PtrDigits: Pointer; <-- The data is somewhere else.
Size: Byte;
MSB: Byte;
Sign: Shortint;
...
class operator Implicit(a: TBigint): TBigint; <<-- is this allowed?
....
The code is pre-class operator legacy code, but I want to add operators.
I know the data should really be stored in a dynamic array of byte, but I do not want to change the code, because all the meat is in x86-assembly.
I want to following code to trigger the class operator at the bottom:
procedure test(a: TBignum);
var b: TBignum;
begin
b:= a; <<-- naive copy will tangle up the `PtrDigit` pointers.
....
If I add the implicit typecast to itself, will the following code be executed?
class operator TBigint.Implicit(a: TBigint): TBigint;
begin
sdpBigint.CreateBigint(Result, a.Size);
sdpBigint.CopyBigint(a, Result);
end;
(Will test and add the answer if it works as I expect).
My first answer attempts to dissuade against the idea of overriding the assignment operator. I still stand by that answer, because many of the problems to be encountered are better solved with objects.
However, David quite rightly pointed out that TBigInt is implemented as a record to leverage operator overloads. I.e. a := b + c;. This is a very good reason to stick with a record based implementation.
Hence, I propose this alternative solution that kills two birds with one stone:
It removes the memory management risks explained in my other answer.
And provides a simple mechanism to implement Copy-on-Write semantics.
(I do still recommend that unless there's a very good reason to retain a record based solution, consider switching to an object based solution.)
The general idea is as follows:
Define an interface to represent the BigInt data. (This can initially be minimalist and support only control of the pointer - as in my example. This would make the initial conversion of existing code easier.)
Define an implementation of the above interface which will be used by the TBigInt record.
The interface solves the first problem, because interfaces are a managed type; and Delphi will dereference the interface when a record goes out of scope. Hence, the underlying object will destroy itself when no longer needed.
The interface also provides the opportunity to solve the second problem, because we can check the RefCount to know whether we should Copy-On-Write.
Note that long term it might prove beneficial to move some of the BigInt implementation from the record to the class & interface.
The following code is trimmed-down "big int" implementation purely to illustrate the concepts. (I.e. The "big" integer is limited to a regular 32-bit number, and only addition has been implemented.)
type
IBigInt = interface
['{1628BA6F-FA21-41B5-81C7-71C336B80A6B}']
function GetData: Pointer;
function GetSize: Integer;
procedure Realloc(ASize: Integer);
function RefCount: Integer;
end;
type
TBigIntImpl = class(TInterfacedObject, IBigInt)
private
FData: Pointer;
FSize: Integer;
protected
{IBigInt}
function GetData: Pointer;
function GetSize: Integer;
procedure Realloc(ASize: Integer);
function RefCount: Integer;
public
constructor CreateCopy(ASource: IBigInt);
destructor Destroy; override;
end;
type
TBigInt = record
PtrDigits: IBigInt;
constructor CreateFromInt(AValue: Integer);
class operator Implicit(AValue: TBigInt): Integer;
class operator Add(AValue1, AValue2: TBigInt): TBigInt;
procedure Add(AValue: Integer);
strict private
procedure CopyOnWriteSharedData;
end;
{ TBigIntImpl }
constructor TBigIntImpl.CreateCopy(ASource: IBigInt);
begin
Realloc(ASource.GetSize);
Move(ASource.GetData^, FData^, FSize);
end;
destructor TBigIntImpl.Destroy;
begin
FreeMem(FData);
inherited;
end;
function TBigIntImpl.GetData: Pointer;
begin
Result := FData;
end;
function TBigIntImpl.GetSize: Integer;
begin
Result := FSize;
end;
procedure TBigIntImpl.Realloc(ASize: Integer);
begin
ReallocMem(FData, ASize);
FSize := ASize;
end;
function TBigIntImpl.RefCount: Integer;
begin
Result := FRefCount;
end;
{ TBigInt }
class operator TBigInt.Add(AValue1, AValue2: TBigInt): TBigInt;
var
LSum: Integer;
begin
LSum := Integer(AValue1) + Integer(AValue2);
Result.CreateFromInt(LSum);
end;
procedure TBigInt.Add(AValue: Integer);
begin
CopyOnWriteSharedData;
PInteger(PtrDigits.GetData)^ := PInteger(PtrDigits.GetData)^ + AValue;
end;
procedure TBigInt.CopyOnWriteSharedData;
begin
if PtrDigits.RefCount > 1 then
begin
PtrDigits := TBigIntImpl.CreateCopy(PtrDigits);
end;
end;
constructor TBigInt.CreateFromInt(AValue: Integer);
begin
PtrDigits := TBigIntImpl.Create;
PtrDigits.Realloc(SizeOf(Integer));
PInteger(PtrDigits.GetData)^ := AValue;
end;
class operator TBigInt.Implicit(AValue: TBigInt): Integer;
begin
Result := PInteger(AValue.PtrDigits.GetData)^;
end;
The following tests were written as I built up the proposed solution. They prove: some basic functionality, that the copy-on-write works as expected, and that there are no memory leaks.
procedure TTestCopyOnWrite.TestCreateFromInt;
var
LBigInt: TBigInt;
begin
LBigInt.CreateFromInt(123);
CheckEquals(123, LBigInt);
//Dispose(PInteger(LBigInt.PtrDigits)); //I only needed this until I
//started using the interface
end;
procedure TTestCopyOnWrite.TestAssignment;
var
LValue1: TBigInt;
LValue2: TBigInt;
begin
LValue1.CreateFromInt(123);
LValue2 := LValue1;
CheckEquals(123, LValue2);
end;
procedure TTestCopyOnWrite.TestAddMethod;
var
LValue1: TBigInt;
begin
LValue1.CreateFromInt(123);
LValue1.Add(111);
CheckEquals(234, LValue1);
end;
procedure TTestCopyOnWrite.TestOperatorAdd;
var
LValue1: TBigInt;
LValue2: TBigInt;
LActualResult: TBigInt;
begin
LValue1.CreateFromInt(123);
LValue2.CreateFromInt(111);
LActualResult := LValue1 + LValue2;
CheckEquals(234, LActualResult);
end;
procedure TTestCopyOnWrite.TestCopyOnWrite;
var
LValue1: TBigInt;
LValue2: TBigInt;
begin
LValue1.CreateFromInt(123);
LValue2 := LValue1;
LValue1.Add(111); { If CopyOnWrite, then LValue2 should not change }
CheckEquals(234, LValue1);
CheckEquals(123, LValue2);
end;
Edit
Added a test demonstrating use of TBigInt as value parameter to a procedure.
procedure TTestCopyOnWrite.TestValueParameter;
procedure CheckValueParameter(ABigInt: TBigInt);
begin
CheckEquals(2, ABigInt.PtrDigits.RefCount);
CheckEquals(123, ABigInt);
ABigInt.Add(111);
CheckEquals(234, ABigInt);
CheckEquals(1, ABigInt.PtrDigits.RefCount);
end;
var
LValue: TBigInt;
begin
LValue.CreateFromInt(123);
CheckValueParameter(LValue);
end;
There is nothing in Delphi that allows you to hook into the assignment process. Delphi has nothing like C++ copy constructors.
Your requirements, are that:
You need a reference to the data, since it is of variable length.
You also have a need for value semantics.
The only types that meet both of those requirements are the native Delphi string types. They are implemented as a reference. But the copy-on-write behaviour that they have gives them value semantics. Since you want an array of bytes, AnsiString is the string type that meets your needs.
Another option would be to simply make your type be immutable. That would let you stop worrying about copying references since the referenced data could never be modified.
It seems to me your TBigInt should be a class rather than a record. Because you're concerned about PtrDigits being tangled up, it sounds like you're needing extra memory management for what the pointer references. Since records don't support destructors there's no automatic management of that memory. Also if you simply declare a variable of TBigInt, but don't call the CreatBigInt constructor, the memory is not correctly initialised. Again, this is because you cannot override a record's default parameterless constructor.
Basically you have to always remember what has been allocated for the record and remember to manually deallocate. Sure you can have a deallocate procedure on the record to help in this regard, but you still have to remember to call it in the correct places.
However that said, you could implement an explicit Copy function, and add an item to your code-review checklist that TBitInt has been copied correctly. Unfortunately you'll have to be very careful with the implied copies such as passing the record via a value parameter to another routine.
The following code illustrates an example conceptually similar to your needs and demonstrates how the CreateCopy function "untangles" the pointer. It also highlights some of the memory management problems that crop up, which is why records are probably not a good way to go.
type
TMyRec = record
A: PInteger;
function CreateCopy: TMyRec;
end;
function TMyRec.CreateCopy: TMyRec;
begin
New(Result.A);
Result.A^ := A^;
end;
var
R1, R2: TMyRec;
begin
New(R1.A); { I have to manually allocate memory for the pointer
before I can use the reocrd properly.
Even if I implement a record constructor to assist, I
still have to remember to call it. }
R1.A^ := 1;
R2 := R1;
R2.A^ := 2; //also changes R1.A^ because pointer is the same (or "tangled")
Writeln(R1.A^);
R2 := R1.CreateCopy;
R2.A^ := 3; //Now R1.A is different pointer so R1.A^ is unchanged
Writeln(R1.A^);
Dispose(R1.A);
Dispose(R2.A); { <-- Note that I have to remember to Dispose the additional
pointer that was allocated in CreateCopy }
end;
In a nutshell, it seems you're trying to sledgehammer records into doing things they're not really suited to doing.
They are great at making exact copies. They have simple memory management: Declare a record variable, and all memory is allocated. Variable goes out of scope and all memory is deallocated.
Edit
An example of how overriding the assignment operator can cause a memory leak.
var
LBigInt: TBigInt;
begin
LBigInt.SetValue(123);
WriteBigInt(LBigInt); { Passing the value by reference or by value depends
on how WriteBigInt is declared. }
end;
procedure WriteBigInt(ABigInt: TBigInt);
//ABigInt is a value parameter.
//This means it will be copied.
//It must use the overridden assignment operator,
// otherwise the point of the override is defeated.
begin
Writeln('The value is: ', ABigInt.ToString);
end;
//If the assignment overload allocated memory, this is the only place where an
//appropriate reference exists to deallocate.
//However, the very last thing you want to do is have method like this calling
//a cleanup routine to deallocate the memory....
//Not only would this litter your code with extra calls to accommodate a
//problematic design, would also create a risk that a simple change to taking
//ABigInt as a const parameter could suddenly lead to Access Violations.

How do we activate (on demand) an object that is a property of another object?

I am implementing an object TTextFile that is a framework for using the low level pascal file function with the OO paradigm. I want to add to developers the option to use it as a TStringList when needed in the same object, like this:
TTextFile = class(TObject)
constructor Create(FileName: String);
procedure OpenForRead;
procedure OpenForWrite;
{...}
property Content: TStringList;
end;
But my problem is that I want the Content property to use user LoadFromFile only at the first time the application uses it. Not in the Create construction, because the file might be too big, and the programmer would prefer to use the other functions in this case. The Content would be use when he knows the file he is using will not be very big.
An example of a big file is a list with all the client names and citizen ID. An example of a very tiny file is that same list, but only with the clients that are waiting to be attended in the current day.
Is it possible to be done in OO pascal? If it is not possible, I will have to make a kind of activation procedure or an overload Create and make the programmer always check if the Content is loaded before use it.
Use the concept of lazy initialization. The first time the Content property is read, load the file contents, but then keep the contents available so that subsequent accesses of the property don't re-read the file.
private
FContent: TStrings;
function GetContent: TStrings;
public
property Content: TStrings read GetContent;
function TTextFile.GetContent: TStrings;
begin
if not Assigned(FContent) then begin
FContent := TStringList.Create;
try
FContent.LoadFromFile(FFileName);
except
FContent.Free;
FContent := nil;
raise;
end;
end;
Result := FContent;
end;
Certainly this is possible.
Change your class declaration:
TTextFile = class(TObject)
constructor Create(FileName: String);
procedure OpenForRead;
procedure OpenForWrite;
function GetContent: TStringList;
{...}
property Content: TStringList read GetContent;
end;
and implement it:
function TTextFile.GetContent: TStringList;
begin
Result := TStringList.Create;
Result.LoadFromFile(FFileName); // Presumes FileName is stored in FFileName in constructor
end;

Storing extra data in developer express component cxTreeView

I have a cxTreeView on a form displaying descriptions of some files. I would like to be able to have the option of clicking one of the descriptions and have the filename returned.
In order to do this I need to store some extra data in the treeview and that is my problem.
How do I do this:
Store both filename and description, display just the description but get the filename by doubleclicking?
I think you can use a TcxTreeList for this task (if possible), if there is no urgent need to use a TcxTreeView. In a TcxTreeList you can add a new column and store the filename in it. Then make this column invisible.
If you must rely on a TcxTreeView, there is no option to directly store data, like strings, in the TTreeNodes. You can store a pointer to any object into the property "Data" of the TTreeNode.
In both answers here is mentioned that TcxTreeView uses standard TTreeNode where, as Warren pointed in his answer as first, you can use the TTreeNode.Data property to store your data. I'll just complete these thoughts with an example.
Update:
The previous version of this post would work only for constant strings for whose the space is being allocated automatically. If you need to pass a variable into the TTreeNode.Data you have to allocate and release the memory by your own. Here is the example with using simple helper functions.
The AddFileNode helper function adds the node to the Items into the ParentNode with the name of the FileDesc and allocates the space and copy the passed FileName for the TTreeNode.Data by using the StrNew function.
When you need to modify the data, you should release (or better, but little bit complicated would be to reallocate) the allocated memory and allocate the space and copy the new value. For this you can use the ChangeFileName which disposes the string memory allocated before and allocates the space and copy the passed FileName value.
As I mentioned before you need to take care of the memory disposal by your own and for this there is the OnDeletion event the best place. So write the handler for this event where you'll release the memory allocated when adding the items.
function AddFileNode(Items: TTreeNodes; ParentNode: TTreeNode;
const FileName, FileDesc: string): TTreeNode;
begin
Result := Items.AddChildObject(ParentNode, FileDesc, StrNew(PChar(FileName)));
end;
function ChangeFileName(TreeNode: TTreeNode; const FileName: string): Boolean;
begin
Result := False;
if Assigned(TreeNode.Data) then
begin
Result := True;
StrDispose(PChar(TreeNode.Data));
TreeNode.Data := StrNew(PChar(FileName));
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
S := 'C:\FileName 1.xxx';
AddFileNode(cxTreeView1.Items, nil, S, 'File 1');
S := 'C:\FileName 2.xxx';
AddFileNode(cxTreeView1.Items, nil, S, 'File 2');
end;
procedure TForm1.cxTreeView1Deletion(Sender: TObject; Node: TTreeNode);
begin
if Assigned(Node.Data) then
StrDispose(PChar(Node.Data));
end;
procedure TForm1.cxTreeView1DblClick(Sender: TObject);
var
CurrentPos: TPoint;
CurrentNode: TTreeNode;
begin
CurrentPos := cxTreeView1.ScreenToClient(Mouse.CursorPos);
if (htOnItem in cxTreeView1.GetHitTestInfoAt(CurrentPos.X, CurrentPos.Y)) then
begin
CurrentNode := cxTreeView1.GetNodeAt(CurrentPos.X, CurrentPos.Y);
if Assigned(CurrentNode) and Assigned(CurrentNode.Data) then
ShowMessage(PChar(CurrentNode.Data));
end;
end;
If the nodes are regular TTreeNode, you can use the Data:Pointer value in the Node object.
If the nodes in your tree are of type TcxTreeListNode, they contain the ability to store any data values you want. WHen I look at the sources, I see these properties in the node-level:
property ValueCount: Integer read GetValueCount;
property Values[Index: Integer]: Variant read GetValue write SetValue;

How to automatically free classes/objects?

What techniques exist to automatically free objects in delphi applications?
Use interfaces instead of objects. They are reference counted and freed automatically when the reference count reaches 0.
I have written a function GC(obj: TObject) (for Garbage Collect) which takes an object and frees it when the execution leaves the current method. It's kind of like a one-line shorthand function for a Try Finally Free block.
Instead of:
procedure Test;
var AQuery: TQuery;
begin
AQuery := TQuery.Create(nil);
try
...
finally
FreeAndNil(AQuery);
end;
end;
I just have:
procedure Test;
var AQuery: TQuery;
begin
AQuery := TQuery.Create(nil);
GC(AQuery);
...
end;
The GC function simply returns an object in the form of an interface.
function GC(obj: TObject): IGarbo;
begin
Result := TGarbo.Create(obj);
end;
Because the TGarbo class descends from TInterfacedObject, when the TGarbo object goes out of scope it will automatically get freed. In the destructor of the TGarbo object, it also frees the object you passed to it in it's constructor (the object you passed in the GC function).
type
IGarbo = interface
['{A6E17957-C233-4433-BCBD-3B53C0C2C596}']
function Obj: TObject;
end;
TGarbo = class(TInterfacedObject, IGarbo)
private
FObj: TObject;
public
constructor Create(AObjectToGC: TObject);
destructor Destroy; override;
function Obj: TObject;
end;
{ TGarbo }
constructor TGarbo.Create(AObjectToGC: TObject);
begin
inherited Create;
FObj := AObjectToGC;
end;
destructor TGarbo.Destroy;
begin
if Assigned(FObj) then
FreeAndNil(FObj);
inherited;
end;
function TGarbo.Obj: TObject;
begin
Result := FObj;
end;
Being stuck in the world of Delphi 7 with no sight of upgrading to a version of Delphi with built-in garbage collection in the near future, I'm addicted to using this short-hand method of easily freeing local temporary objects! :)
Along the lines of interfaces, you can try the Guard function in the JclSysUtils unit, part of the free Jedi Code Library. It allows you to associate an object with a separate interface reference, so when that interface reference is destroyed, the object is destroyed along with it. This can be useful when you don't have the option of modifying the classes you're using to make them support interfaces of their own.
var
G: ISafeGuard;
foo: TStrings;
begin
// Guard returns TObject, so a type-cast is necessary
foo := Guard(TStringList.Create, G) as TStrings;
// Use the object as normal
foo.Add('bar');
end; // foo gets freed automatically as G goes out of scope
There are overloads for objects and GetMem-allocated pointers. There is also IMultiSafeGuard, which can ensure that multiple objects get freed.
If you have a factory function, you might be creating an object, setting some of its properties, and then returning it. If an exception occurs while setting the properties, you'll want to make sure you free the object since you can't return it. One way to do that is like this:
function Slurp(const source: TFileName): TStrings;
begin
Result := TStringList.Create;
try
Result.LoadFromFile(source);
except
Result.Free;
raise;
end;
end;
With Guard, it would become this:
function Slurp(const source: TFileName): TStrings;
var
G: ISafeGuard;
begin
Result := Guard(TStringList.Create, G) as TStrings;
Result.LoadFromFile(source);
G.ReleaseItem;
end;
The ReleaseItem method revokes the ISafeGuard's ownership of the object. If an exception occurs before that happens, then as the stack unwinds and the interface is released, the guard will free the object.
I have to say, I don't like "hiding" the Free of an object. Far better to have the traditional code:
MyObject := TObject.Create;
try
// do stuff
finally
FreeAndNil(MyObject);
end;
No way it can go wrong, works as expected, and people recognise the pattern.
Use the object ownership of components that the VCL provides. As long as you create objects with a non-nil owner you don't need to free them explicitely. See also my answer to this question.
Here is the API for Boehm Garbage Collector DLL for Delphi. The Delphi API is written by Barry Kelly, who works for CodeGear writing the compiler now.
Smart Pointers work really well if you have Delphi 2009.
If you use Delphi for .Net / Delphi Prism you get Garbage Collection which takes care of all the freeing.

Resources