I want to pass an object A to a second object B, have B do some processing and finally release A in case it's not needed anymore. A watered down version is given below.
program Project6;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyObject = class(TObject)
public
FField1: string;
FField2: string;
end;
TBigObject = class(TObject)
public
FMyObject: TMyObject;
procedure Bind(var MyObject: TMyObject);
procedure Free();
end;
procedure TBigObject.Bind(var MyObject: TMyObject);
begin
FMyObject := MyObject;
end;
procedure TBigObject.Free;
begin
FreeAndNil(FMyObject);
Destroy();
end;
var
MyObject: TMyObject;
BigObject: TBigObject;
begin
try
MyObject := TMyObject.Create();
BigObject := TBigObject.Create();
BigObject.Bind(MyObject);
BigObject.Free();
if (Assigned(MyObject)) then begin
WriteLn('Set MyObject free!');
MyObject.Free();
end;
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
(Never mind the awful design.) Now, what I don't understand is why FreeAndNil actually does free MyObject, yet Assigned(MyObject) is evaluated to true (giving an AV at MyObject.Free()).
Could someone please help enlighten me?
MyObject is a different variable from the field FMyObject. And you're only niling the field FMyObject.
FreeAndNil frees to object pointed to, and nils the variable you passed in. It doesn't magically discover and nil all other variables that point to the object you freed.
FreeAndNil(FMyObject); does the same thing as:
object(FMyObject).Free();
FMyObject=nil;
(Technically this is not entirely correct, the cast to object is a reinterpret cast due to the untyped var parameter, but that's not relevant here)
And that obviously only modifies FMyObject and not MyObject
Oh I just noticed that you're hiding the original Free method? That's insane. FreeAndNil still uses the original Free. That doesn't hit you in your example because you call Free on a variable with the static type TBigObject and not FreeAndNil. But it's a receipt for disaster.
You should instead override the destructor Destroy.
The reason is simple, you nil one reference but not the other. Consider this example:
var
Obj1, Obj2: TObject;
begin
Obj1 := TObject.Create;
Obj2 := Obj1;
FreeAndNil(Obj1);
// Obj1 is released and nil, Obj2 is non-nil but now points to undefined memory
// ie. accessing it will cause access violations
end;
You have two copies of the reference to the object but are only setting one of them to nil. Your code is equivalent to this:
i := 1;
j := i;
i := 0;
Writeln(j);//outputs 1
I'm using integers in this example because I'm sure you are familiar with how they work. Object references, which are really just pointers, behave in exactly the same way.
Casting the example in terms of object references makes it look like this:
obj1 := TObject.Create;
obj2 := obj1;
obj1.Free;//these two lines are
obj1 := nil;//equivalent to FreeAndNil
//but obj2 still refers to the destroyed object
Aside: You should never call Destroy directly and never declare a method called Free. Instead override Destroy and call the static Free defined in TObject, or indeed FreeAndNil.
There are a few peculiarities in your code.
First, you should not re-write Free, you should override the virtual destructor (Destroy) of your class.
But ISTM that BigObject is not the owner of MyObject, so BigObject should not try to free it at all.
As CodeInChaos already said, FreeAndNil only frees one variable, in this case the FMyObject field. FreeAndNil is not required anyway, since nothing can happen after the object was freed.
Assigned can't be used to check if an object was freed already. It can only check for nil, and FreeAndNil only sets one reference to nil, not the object itself (this is impossible).
Your program design should be thus, that an object can and will only be freed if nothing is accessing it anymore.
Related
In my program I do:
var aObj: Tobject;
var aObjClassType: Tclass;
....
aObjClassType := aObj.ClassType;
....
aObj.free;
aObj := nil;
....
showmessage(aObjClassType.Classname);
this work but I m not quite sure If this is correct, especially when i read the function TObject.ClassType
function TObject.ClassType: TClass;
begin
Pointer(Result) := PPointer(Self)^;
end;
So does freeing aObj will not also free the aObjClassType ?
A TClass is a class. A TObject is an instance. So obj.ClassType returns the class, that is the type, of the instance obj.
Note that this is the runtime type of the instance rather than the type of the obj reference variable. This is relevant when using polymorphism. So if you write
var
shape: TShape;
....
shape := TSquare.Create;
Then shape.ClassType returns TSquare even though the shape variable is TShape.
So does freeing aObj will not also free the aObjClassType?
No. Classes are static and created when the module loads and destroyed when the module unloads.
For more detail read the documentation: http://docwiki.embarcadero.com/RADStudio/en/Classes_and_Objects_(Delphi)#TObject_and_TClass
I have method with a parameter as object (sniped code below):
TMyObject=class(TObject)
constructor Create();
destructor Destroy();override;
end;
implementation
function doSomething(x:TMyObject):integer;
begin
//code
end;
procedure test();
var
w:integer;
begin
w:=doSomething(TMyObject.Create);
//here: how to free the created object in line above?
end;
How destroy object created inside of called method doSomething outside of this method?
In order to free the object instance, you need to have a reference to it on which you can call Free().
Since you are creating the object instance in-place as a parameter, the only reference you will have is the one inside of the doSomething() parameter.
You either have to Free it inside of doSomething() (which is practice I would not advise doing):
function doSomething(x: TMyObject): Integer;
begin
try
//code
finally
x.Free;
end;
end;
Or, you need to create an additional variable in test(), pass it to doSomething(), and then Free it after doSomething() returns:
procedure test();
var
w: Integer;
o: TMyObject
begin
o := TMyObject.Create;
try
w := doSomething(o);
finally
o.Free;
end;
end;
While one may think that using a reference counted object would allow you to create the object in-place and let reference counting free the object, this kind of construction may not work because of the following compiler issue:
The compiler should keep a hidden reference when passing freshly created object instances directly as const interface parameters
This is confirmed by a former Embarcadero compiler engineer, Barry Kelly, in a StackOverflow answer:
Should the compiler hint/warn when passing object instances directly as const interface parameters?
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.
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.
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.