Why should I not use "if Assigned()" before accessing objects? - delphi

This question is a continuance of a particular comment from people on stackoverflow which I've seen a few different times now. I, along with the developer who taught me Delphi, in order to keep things safe, have always put a check if assigned() before freeing objects, and before doing other various things. However, I'm now told that I should not be adding this check. I'd like to know if there is any difference in how the application compiles/runs if I do this, or if it won't affect the result at all...
if assigned(SomeObject) then SomeObject.Free;
Let's say I have a form, and I'm creating a bitmap object in the background upon the form's creation, and freeing it when I'm done with it. Now I guess my problem is I got too used to putting this check on a lot of my code when I'm trying to access objects which might potentially have been free'd at some point. I've been using it even when it's not necessary. I like to be thorough...
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FBitmap: TBitmap;
public
function LoadBitmap(const Filename: String): Bool;
property Bitmap: TBitmap read FBitmap;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FBitmap:= TBitmap.Create;
LoadBitmap('C:\Some Sample Bitmap.bmp');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if assigned(FBitmap) then begin //<-----
//Do some routine to close file
FBitmap.Free;
end;
end;
function TForm1.LoadBitmap(const Filename: String): Bool;
var
EM: String;
function CheckFile: Bool;
begin
Result:= False;
//Check validity of file, return True if valid bitmap, etc.
end;
begin
Result:= False;
EM:= '';
if assigned(FBitmap) then begin //<-----
if FileExists(Filename) then begin
if CheckFile then begin
try
FBitmap.LoadFromFile(Filename);
except
on e: exception do begin
EM:= EM + 'Failure loading bitmap: ' + e.Message + #10;
end;
end;
end else begin
EM:= EM + 'Specified file is not a valid bitmap.' + #10;
end;
end else begin
EM:= EM + 'Specified filename does not exist.' + #10;
end;
end else begin
EM:= EM + 'Bitmap object is not assigned.' + #10;
end;
if EM <> '' then begin
raise Exception.Create('Failed to load bitmap: ' + #10 + EM);
end;
end;
end.
Now let's say I'm introducing a new custom list object called TMyList of TMyListItem. For each item in this list, of course I have to create/free each item object. There's a few different ways of creating an item, as well as a few different ways of destroying an item (Add/Delete being the most common). I'm sure it's a very good practice to put this protection here...
procedure TMyList.Delete(const Index: Integer);
var
I: TMyListItem;
begin
if (Index >= 0) and (Index < FItems.Count) then begin
I:= TMyListItem(FItems.Objects[Index]);
if assigned(I) then begin //<-----
if I <> nil then begin
I.DoSomethingBeforeFreeing('Some Param');
I.Free;
end;
end;
FItems.Delete(Index);
end else begin
raise Exception.Create('My object index out of bounds ('+IntToStr(Index)+')');
end;
end;
In many scenarios, at least I would hope that the object is still created before I try to free it. But you never know what slips might happen in the future where an object gets free'd before it's supposed to. I've always used this check, but now I'm being told I shouldn't, and I still don't understand why.
EDIT
Here's an example to try to explain to you why I have a habit of doing this:
procedure TForm1.FormDestroy(Sender: TObject);
begin
SomeCreatedObject.Free;
if SomeCreatedObject = nil then
ShowMessage('Object is nil')
else
ShowMessage('Object is not nil');
end;
My point is that if SomeCreatedObject <> nil is not the same as if Assigned(SomeCreatedObject) because after freeing SomeCreatedObject, it does not evaluate to nil. So both checks should be necessary.

This is a very broad question with many different angles.
The meaning of the Assigned function
Much of the code in your question betrays an incorrect understanding of the Assigned function. The documentation states this:
Tests for a nil (unassigned) pointer or procedural variable.
Use Assigned to determine whether the pointer or the procedure
referenced by P is nil. P must be a variable reference of a pointer or
procedural type.
Assigned(P) corresponds to the test P <> nil for a pointer variable,
and #P <> nil for a procedural variable.
Assigned returns False if P is nil, True otherwise.
Tip: When testing object events and procedures for assignment, you
cannot test for nil, and using Assigned is the right way.
....
Note: Assigned cannot detect a dangling pointer--that is, one that is not nil, but that no longer points to valid data.
The meaning of Assigned differs for pointer and procedural variables. In the rest of this answer we will consider pointer variables only, since that is the context of the question. Note that an object reference is implemented as a pointer variable.
The key points to take from the documentation are that, for pointer variables:
Assigned is equivalent to testing <> nil.
Assigned cannot detect whether the pointer or object reference is valid or not.
What this means in the context of this question is that
if obj<>nil
and
if Assigned(obj)
are completely interchangeable.
Testing Assigned before calling Free
The implementation of TObject.Free is very special.
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
This allows you to call Free on an object reference that is nil and doing so has no effect. For what it is worth, I am aware of no other place in the RTL/VCL where such a trick is used.
The reason why you would want to allow Free to be called on a nil object reference stems from the way constructors and destructors operate in Delphi.
When an exception is raised in a constructor, the destructor is called. This is done in order to deallocate any resources that were allocated in that part of the constructor that succeeded. If Free was not implemented as it is then destructors would have to look like this:
if obj1 <> nil then
obj1.Free;
if obj2 <> nil then
obj2.Free;
if obj3 <> nil then
obj3.Free;
....
The next piece of the jigsaw is that Delphi constructors initialise the instance memory to zero. This means that any unassigned object reference fields are nil.
Put this all together and the destructor code now becomes
obj1.Free;
obj2.Free;
obj3.Free;
....
You should choose the latter option because it is much more readable.
There is one scenario where you need to test if the reference is assigned in a destructor. If you need to call any method on the object before destroying it then clearly you must guard against the possibility of it being nil. So this code would run the risk of an AV if it appeared in a destructor:
FSettings.Save;
FSettings.Free;
Instead you write
if Assigned(FSettings) then
begin
FSettings.Save;
FSettings.Free;
end;
Testing Assigned outside a destructor
You also talk about writing defensive code outside a destructor. For example:
constructor TMyObject.Create;
begin
inherited;
FSettings := TSettings.Create;
end;
destructor TMyObject.Destroy;
begin
FSettings.Free;
inherited;
end;
procedure TMyObject.Update;
begin
if Assigned(FSettings) then
FSettings.Update;
end;
In this situation there is again no need to test Assigned in TMyObject.Update. The reason being that you simply cannot call TMyObject.Update unless the constructor of TMyObject succeeded. And if the constructor of TMyObject succeeded then you know for sure that FSettings was assigned. So again you make your code much less readable and harder to maintain by putting in spurious calls to Assigned.
There is a scenario where you need to write if Assigned and that is where the existence of the object in question is optional. For example
constructor TMyObject.Create(UseLogging: Boolean);
begin
inherited Create;
if UseLogging then
FLogger := TLogger.Create;
end;
destructor TMyObject.Destroy;
begin
FLogger.Free;
inherited;
end;
procedure TMyObject.FlushLog;
begin
if Assigned(FLogger) then
FLogger.Flush;
end;
In this scenario the class supports two modes of operation, with and without logging. The decision is taken at construction time and any methods which refer to the logging object must test for its existence.
This not uncommon form of code makes it even more important that you don't use spurious calls to Assigned for non-optional objects. When you see if Assigned(FLogger) in code that should be a clear indication to you that the class can operate normally with FLogger not in existence. If you spray gratuitous calls to Assigned around your code then you lose the ability to tell at a glance whether or not an object should always exist.

Free has some special logic: it checks to see whether Self is nil, and if so, it returns without doing anything -- so you can safely call X.Free even if X is nil. This is important when you're writing destructors -- David has more details in his answer.
You can look at the source code for Free to see how it works. I don't have the Delphi source handy, but it's something like this:
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
Or, if you prefer, you could think of it as the equivalent code using Assigned:
procedure TObject.Free;
begin
if Assigned(Self) then
Destroy;
end;
You can write your own methods that check for if Self <> nil, as long as they're static (i.e., not virtual or dynamic) instance methods (thanks to David Heffernan for the documentation link). But in the Delphi library, Free is the only method I know of that uses this trick.
So you don't need to check to see if the variable is Assigned before you call Free; it already does that for you. That's actually why the recommendation is to call Free rather than calling Destroy directly: if you called Destroy on a nil reference, you would get an access violation.

Why you shouldn't call
if Assigned(SomeObject) then
SomeObject.Free;
Simply because you would execute something like this
if Assigned(SomeObject) then
if Assigned(SomeObject) then
SomeObject.Destroy;
If you call just SomeObject.Free; then it's just
if Assigned(SomeObject) then
SomeObject.Destroy;
To your update, if you afraid of the object instance reference use FreeAndNil. It will destroy and dereference your object
FreeAndNil(SomeObject);
It's similar as if you call
SomeObject.Free;
SomeObject := nil;

I create a simple example showing my procedure:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Cir : TCircle;
implementation
{$R *.fmx}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(Cir) then
begin
Cir.Free;
Cir := Nil;
end;
Cir := TCircle.Create(nil);
with Cir do
begin
Parent := Form1;
Height := 50;
Width := 50;
Position.X := 100;
Position.Y := 100;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if Assigned(Cir) then
begin
Cir.Free;
Cir := Nil;
end;
end;
end.

I'm not completely sure of that, but it seems:
if assigned(object.owner) then object.free
works fine. In this example it would be
if assigned(FBitmap.owner) then FBitmap.free

Related

DynArraySize() works correctly only up to arrays of 649 integer elements

I experienced a RTTI-related problem in Delphi 10.2 Update 2 and was able to track it down to a fewer amount of code (see below).
I have some TPersistent-descendant class TMyObj that publishes a property of type TArray<Integer>. When I recieve its value via GetDynArrayProp() and query its size via DynArraySize() this works only up to a size of exactly 649 elements. Above this special count some very big size value is returned.
Note, that my array is generated from an instance of TDictionary<Integer,Boolean>'s Keys property with its very own ToArray method.
I also tried to modify TMyObj.GetDynArray so that it returns a instance of TArray<Integer> directly and it worked correctly.
Thus, I think that could correlate in some mystical way.
What is wrong with my use of DynArraySize()? What's behind this mystical behaviour of dynamic arrays?
program RTTIPropDynArray;
{$APPTYPE CONSOLE}
uses
System.Classes, System.Generics.Collections, System.SysUtils, System.TypInfo;
type
TMyDict = TDictionary<Integer,Boolean>;
TMyArray = TArray<Integer>;
TMyObj = class(TPersistent)
private
FValues: TMyDict;
function GetDynArray: TMyArray;
public
constructor Create(const ACount: Integer);
destructor Destroy; override;
published
property DynArray: TMyArray read GetDynArray;
end;
{ TMyObj }
constructor TMyObj.Create(const ACount: Integer);
begin
FValues := TMyDict.Create;
while FValues.Count < ACount do
FValues.AddOrSetValue(Random(MaxInt), False);
end;
destructor TMyObj.Destroy;
begin
FreeAndNil(FValues);
inherited;
end;
function TMyObj.GetDynArray: TMyArray;
begin
Result := FValues.Keys.ToArray;
end;
function Test(const ACount: Integer): Boolean;
var
LInstance: TMyObj;
LExpectedSize: Integer;
LDynArraySize: Integer;
begin
LInstance := TMyObj.Create(ACount);
try
LExpectedSize := Length(LInstance.DynArray);
LDynArraySize := DynArraySize(GetDynArrayProp(LInstance, 'DynArray'));
Result := LExpectedSize = LDynArraySize;
if not Result then
WriteLn(Format('Expected size: %d; DynArraySize: %d', [LExpectedSize, LDynArraySize]));
finally
LInstance.Free;
end;
end;
var
LCount: Integer;
begin
Randomize;
LCount := 1;
while Test(LCount) do
Inc(LCount);
ReadLn;
end.
Short answer: Your code is broken
Long answer:
The call to the getter is creating a new array (see TEnumerable<T>.ToArrayImpl in System.Generics.Collections.pas) which is being deallocated in the epilogue of System.TypInfo.GetDynArrayProp (put a breakpoint there and look into the disassembler - it shows #DynArrayClear). Since there is no other reference to this array its memory gets deallocated (if you step into System.pas further you will see that it eventually ends up in _FreeMem). That means every call to this function is returning a dangling pointer!
Now why do you get correct results in all prior calls? Coincidence - the memory has not been reallocated by anything else.
Two possible solutions come into mind that don't involve rewriting the getter:
use the RTTI from System.Rtti.pas as TValue keeps the reference alive
write your own version of GetDynArrayProp that keeps the reference alive - but you have to make sure to always call DynArrayClear after or you create memory leaks
Personally I would use the first one.

Avoid that SetFocus raises an Exception

I am working at a huge, legacy source code where several SetFocus is called at many places, but sometimes, the check if the control is visible or enabled is missing.
Due to limited time, and the huge amount of source code, I decided that I want to ignore these errors, since the focus is (in our case) not a critical feature. A raised Exception will result in a complete failure, while a missing focus is just an optical issue.
My current plan is following:
I create an unit with a class helper like this:
type
TWinControlEx = class helper for TWinControl
procedure SetFocusSafe;
end;
procedure TWinControlEx.SetFocusSafe;
begin
if CanFocus then SetFocus;
end;
I include the unit to every unit which uses ".SetFocus" (I will use the global code search)
I replace every .SetFocus with .SetFocusSafe
There is a problem though: If possible, I want to avoid that coworkers accidently use .SetFocus , or forget to include the classhelper unit.
Which other options do I have?
The best case would be if there is a technique/hack to make SetFocus not raising an exception. (Without recompiling the VCL)
Just patch the TWinControl.SetFocus method:
unit SetFocusFix;
interface
implementation
uses
Controls,
Forms,
SysUtils,
Windows;
type
TWinControlHack = class(TWinControl)
public
procedure SetFocus; override;
end;
procedure TWinControlHack.SetFocus;
var
Parent: TCustomForm;
begin
if not CanFocus then Exit;
Parent := GetParentForm(Self);
if Parent <> nil then
Parent.FocusControl(Self)
else if ParentWindow <> 0 then
Windows.SetFocus(Handle)
else
ValidParentForm(Self);
end;
procedure RedirectFunction(OrgProc, NewProc: Pointer);
type
TJmpBuffer = packed record
Jmp: Byte;
Offset: Integer;
end;
var
n: UINT_PTR;
JmpBuffer: TJmpBuffer;
begin
JmpBuffer.Jmp := $E9;
JmpBuffer.Offset := PByte(NewProc) - (PByte(OrgProc) + 5);
if not WriteProcessMemory(GetCurrentProcess, OrgProc, #JmpBuffer, SizeOf(JmpBuffer), n) then
RaiseLastOSError;
end;
initialization
RedirectFunction(#TWinControl.SetFocus, #TWinControlHack.SetFocus);
end.
Alternatively
TWinControlEx = class helper for TWinControl
procedure SetFocus; reintroduce;
end;
with...
procedure TWinControlEx.SetFocus;
var
Parent: TCustomForm;
begin
if not CanFocus then Exit;
Parent := GetParentForm(Self);
if Parent <> nil then
Parent.FocusControl(Self)
else if ParentWindow <> 0 then
Winapi.Windows.SetFocus(Handle)
else
ValidParentForm(Self);
end;
My answer below does not answer DIRECTLY your question but it is still relevant because you rely on CanFocus. CanFocus returns a lie. You should not rely on it. The documentation is also wrong. More exactly, CanFocus can return True even if the control is not focusable. In this case an exception will be raised.
So, use this instead:
function CanFocus(Control: TWinControl): Boolean;
begin
Result:= Control.CanFocus AND Control.Enabled AND Control.Visible;
if Result
AND NOT Control.InheritsFrom(TForm)
then
{ Recursive call:
This control might be hosted by a panel which could be also invisible/disabled.
So, we need to check all the parents down the road, until we encounter the parent Form.
Also see: GetParentForm }
Result:= CanFocus(Control.Parent); { Parent of a control could be nil, but in this case Control.CanFocus will deal with that.}
end;
procedure SetFocus(Control: TWinControl);
begin
if CanFocus(Control)
then Control.SetFocus;
end;
PS: Under Lazarus CanFocus works properly.
Justification:
J provided a nice answer, but I don't like class helpers because if you have more than one class helper for the same class, the only one will be used. The process is almost "by dice": the order of the units in the "uses" clause determine which helper will apply. I don't like this amount of randomness in a programming language.

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;

Why there's a mem leak and how to fix it?

unit Unit7;
interface
uses Classes;
type
TListener = class(TThread)
procedure Execute; override;
end;
TMyClass = class
o1,o2: Tobject;
procedure FreeMyObject(var obj: TObject);
constructor Create;
destructor Destroy; override;
end;
implementation
uses Windows, SysUtils;
var l: TListener;
my: TMyClass;
procedure TListener.Execute;
var msg:TMsg;
begin
while(GetMessage(msg, Cardinal(-1), 0, 0)) do
if(msg.message=6) then begin
TMyClass(msg.wParam).FreeMyObject(TObject(msg.lParam));
Exit;
end;
end;
constructor TMyClass.Create;
begin
inherited;
o1:=TObject.Create;
o2:=Tobject.Create; // Invalid pointer operation => mem leak
end;
destructor TMyClass.Destroy;
begin
if(Assigned(o1)) then o1.Free;
if(Assigned(o2)) then o2.Free;
inherited;
end;
procedure TMyClass.FreeMyObject(var obj: TObject);
begin
FreeAndNil(obj);
end;
initialization
l:= TListener.Create();
my:=TMyClass.Create;
sleep(1000); //make sure the message loop is set
PostThreadMessage(l.ThreadID, 6, Integer(my), Integer(my.o2));
finalization
l.Free;
my.Free;
end.
I used the message handler to illustrate my problem as is so you understand it. The real design is a lot more complicated. The function 'FreeMyObject' actually Frees AND creates an instance using polymorphism paradigm, but this here is not needed. I only want to point out that the design should stay the same.
Now the question and problem - why it happens AND how to fix it? It seems 'if Assigned(o2)' doesn't fit it.
What I think of: Sending a pointer to my.o2 would free and nil o2 and I tries to do so, but I couldn't convert from pointer to object in the message handler, got no idea why.
Could anybody give a hand? Thanks
You free o2 twice. Once as a result of the message and once from the destructor.
You think you are setting o2 to nil when you call FreeMyObject but you are not. You are in fact setting msg.lParam to 0.
o2 is a variable holding a reference to an object. You are passing the value of o2 and when you pass by value you cannot modify the variable whose value you passed. So you need to pass a reference to o2. To do so you need to add an extra level of redirection and pass a pointer to o2, like so:
if(msg.message=6) then begin
FreeAndNil(PObject(msg.lParam)^);
Exit;
end;
...
PostThreadMessage(l.ThreadID, 6, 0, LPARAM(#my.o2));
You don't need FreeMyObject, you can just call FreeAndNil directly. And you don't need to pass an instance in the message.
I hope your real code isn't quite as weird as this! ;-)
If you want to FreeAndNil an object sending just object reference Integer(my.o2) is not enough - you need Integer(#my.o2). You should also make corresponding changes in your code.
Since your code is difficult to debug I have written a simple demo to give an idea of necessary code changes:
type
PObject = ^TObject;
procedure FreeObj(PObj: PObject);
var
Temp: TObject;
begin
Temp:= PObj^;
PObj^:= nil;
Temp.Free;
end;
procedure TForm17.Button1Click(Sender: TObject);
var
Obj: TList;
PObj: PObject;
begin
Obj:= TList.Create;
PObj:= #Obj;
Assert(Obj <> nil);
FreeObj(PObj);
Assert(Obj = nil);
end;
Here's what's going on:
Program starts. Initialization runs and sends a message to the thread, which calls FreeAndNil on the reference that gets passed in. This sets the reference that gets passed in to nil, but it does not set the object field holding o2 to nil. That's a different reference.
Then in the destructor, since the field isn't nil, it tries to free it again and you get a double-free error (invalid pointer operation exception). Since you raised an exception in the destructor, the TMyClass never gets destroyed and you get a memory leak from it.
If you want to do this right, pass an identifier of some type to FreeMyObject instead of a reference. Like an integer 2, or a string o2. Then have FreeMyObject use this value to look up what it should be calling FreeAndNil on. (If you have Delphi 2010 or later, that's pretty easy to do with RTTI.) It's a little more work, but it will fix the errors you're seeing.

which is the best way to control a object-field type life-cycle?

TMyClass = class(TObject)
private
FMyObject: TObject;
function GetMyObject: TObject;
public
property MyObject: TObject read GetMyObject write FMyObject;
end;
function TMyClass.GetMyObject: TObject;
begin
if FMyObject = nil then
FMyObject := TObject.Create;
Result := FMyObject;
end;
Sometimes, "MyObject" is not created internally but externally created and assigned to the parameter. If this object is created externally, I can not free it in this context.
Should I create a TList and Add in all objects that were created internally and destroy everything on the destructor?
How can I control the lifetime of a parameter if it is created internally or not? What you suggest to do? Is there any pattern to do this?
I'd set a flag in the Property Setter
procedure TMyClass.SetMyObject(AObject: TObject);
begin
if Assigned(MyObject) and FIsMyObject then
FMyObject.Free;
FIsMyObject := False;
FMyObject := AObject;
end;
function TMyClass.GetMyObject: TObject;
begin
if FMyObject = nil then
begin
FMyObject := TObject.Create;
FIsMyObject := True;
end;
Result := FMyObject;
end;
Destructor TMyClass.Destroy;
begin
if FIsMyObject then
FMyObject.Free;
end;
I guess the best would be to redesign your code so that this problem won't arise - this kind of ownership ambiguity is a mess.
Anyway, one option would be to use (reference counted) interfaces. This is problematic in case of circular references.
If the externally created object must not be the only reference then you could still create internal copy of the object, something like
procedure TMyClass.SetMyObject(const Value: TObject);
begin
MyObject.Assign(Value);
end;
You could assign external object to different field than internal and then you don't Free that field in destructor. Or set a flag in the property setter so that you know not to free the external object...
The two most logical and practical solutions (keep a flag, copy on assignment) are already given, but for completeness sake and since the object field isn't likely to be of the TObject type, here are three other approaches. The practicality of these depends on the type of the object field, whether you really don't want an extra boolean flag and whether you dislike to add some intelligent behavior to this construction.
(Warning: this may be a little farfetched.)
Test if the object field is of your private object type:
property MyObject: TSomeAncestor read GetMyObject write SetMyObject;
end;
implementation
type
TMyObject = class(TSomeAncestor) ... end;
destructor TMyClass.Destroy;
begin
if FMyObject is TMyObject then
FMyObject.Free;
Test on the ownership of the object field:
property MyObject: TOwnedObject read GetMyObject write SetMyObject;
end;
implementation
destructor TMyClass.Destroy;
begin
if FMyObject.Owner = Self then
FMyObject.Free;
This construction is especially useful if the external object should anyway be freed by this class: just set its Owner to this class instance. The decision depends no longer on the internal or external creation of the object.
If the object field descends from TComponent, then you do not have to free at all.

Resources