lavitz
asked on
Invoking freed object's function
Hi,
Iam using Delphi 7.
I need to call function on object i.e
try
FObj.fun();
except
// something
end;
procedure fun();
begin
//some code
end;
Problem is that sometimes FObj is freed and delphi invoke this function anyway. But invoke it only some part of it. Result is that i have cursor with hourglass becouse at the begining of fun() is code that change cursor.
I search for solution that prevent call freed function. Or how i can check this.
I know that i can do FreeAndNil on FObj.
Iam using Delphi 7.
I need to call function on object i.e
try
FObj.fun();
except
// something
end;
procedure fun();
begin
//some code
end;
Problem is that sometimes FObj is freed and delphi invoke this function anyway. But invoke it only some part of it. Result is that i have cursor with hourglass becouse at the begining of fun() is code that change cursor.
I search for solution that prevent call freed function. Or how i can check this.
I know that i can do FreeAndNil on FObj.
isn't it a handler being executed ?
probably from a timer ?
procedure TTimer1.OnTimer(Sender: TObject);
begin
ShowMessage(Test);
end;
procedure TTimer2.OnTimer(Sender: TObject);
begin
Sleep(2000);
FreeAndNil(Timer1);
Sleep(5000);
end;
Event for Timer1 is on the stack (interval = default 1 sec)
This causes the same problem
The solution is to remove the timer events from the event stack
probably from a timer ?
procedure TTimer1.OnTimer(Sender: TObject);
begin
ShowMessage(Test);
end;
procedure TTimer2.OnTimer(Sender: TObject);
begin
Sleep(2000);
FreeAndNil(Timer1);
Sleep(5000);
end;
Event for Timer1 is on the stack (interval = default 1 sec)
This causes the same problem
The solution is to remove the timer events from the event stack
ASKER
procedure TMyObject.Fun;
begin
Screen.Cursor := crHourGlass;
try
Raise Exception.Create('Hourglas s problem');
finally
//<---- 1
Screen.Cursor := crDefault;
end;
end;
Its close to my code but in place of //<---- 1 i have EndUpdate that cause access violation and Screen.Cursor := crDefault; is not executed. Ofcourse this happen when object not exists
Ofcouse i could move crDefault to the top but for now i search how to prevent to execut that code.
If its not possible i'll have to change concept of my project.
I have event manager. Some forms can register to EM and EM loop through registered 'events' and call callback function. But forms are not without errors so in some case i have leak, object is freed but still connected to EM.
begin
Screen.Cursor := crHourGlass;
try
Raise Exception.Create('Hourglas
finally
//<---- 1
Screen.Cursor := crDefault;
end;
end;
Its close to my code but in place of //<---- 1 i have EndUpdate that cause access violation and Screen.Cursor := crDefault; is not executed. Ofcourse this happen when object not exists
Ofcouse i could move crDefault to the top but for now i search how to prevent to execut that code.
If its not possible i'll have to change concept of my project.
I have event manager. Some forms can register to EM and EM loop through registered 'events' and call callback function. But forms are not without errors so in some case i have leak, object is freed but still connected to EM.
> Problem is that sometimes FObj is freed and delphi invoke this function anyway.
Delphi will do whatever you ask it to do.
For example:
FObj: TObj;
procedure BadProc();
begin
FObj := TObj.Create();
FObj.fun(); // this is fine.
FreeAndNil(FObj);
FObj.fun(); // this is not. But you have asked Delphi to run it anyway.
end;
What is actually happening is that the .fun member function is a specific offset in the TObj memory structure. Even though FObj is now nil, Delphi will still go to the TObj class memory location for .fun and call it against the instance variable pointed to by the memory location of FObj. Since it has been nil-ed, it could point to anywhere and you could either corrupt some memory or cause an Access Violation.
Delphi will do whatever you ask it to do.
For example:
FObj: TObj;
procedure BadProc();
begin
FObj := TObj.Create();
FObj.fun(); // this is fine.
FreeAndNil(FObj);
FObj.fun(); // this is not. But you have asked Delphi to run it anyway.
end;
What is actually happening is that the .fun member function is a specific offset in the TObj memory structure. Even though FObj is now nil, Delphi will still go to the TObj class memory location for .fun and call it against the instance variable pointed to by the memory location of FObj. Since it has been nil-ed, it could point to anywhere and you could either corrupt some memory or cause an Access Violation.
> object is freed but still connected to EM.
I think what you mean is that
EM was passed a pointer to object
object was freed, so the pointer is no longer referencing an instance of object class
EM tries to call member function of object via pointer
The object should retain a link (private property) to the EM, and on Destroy (override) disconnect itself from EM.
I think what you mean is that
EM was passed a pointer to object
object was freed, so the pointer is no longer referencing an instance of object class
EM tries to call member function of object via pointer
The object should retain a link (private property) to the EM, and on Destroy (override) disconnect itself from EM.
doesn't using if Assigned(Obj) help ?
if Assigned(fObj) then
fObj.Fun;
if Assigned(fObj) then
fObj.Fun;
> doesn't using if Assigned(Obj) help ?
No it doesn't. Try it.
It is the reason why there is a FreeAndNil function.
Freeing an object does not nil all references to the object.
EM is still holding a pointer to some location. Nothing has set the pointer to nil, which is what Assigned() checks.
No it doesn't. Try it.
It is the reason why there is a FreeAndNil function.
Freeing an object does not nil all references to the object.
EM is still holding a pointer to some location. Nothing has set the pointer to nil, which is what Assigned() checks.
yes, that's just why FreeAndNil should be used
Since I found FreeAndNil and the problems it solved
it's the only thing i use anymore to free objects
just using Obj.Free causes a lot of hassle like this
Since I found FreeAndNil and the problems it solved
it's the only thing i use anymore to free objects
just using Obj.Free causes a lot of hassle like this
Unless EM is in the same object and accesses the FObj private variable, FreeAndNil(FObj) does nothing towards clearing the reference EM has to the object.
TEventManager = class....
property Obj: TObj; read FObj; read FObj;
procedure BadProc();
var
anObj := TObj;
em: TEventManager;
begin
anObj := TObj.Create();
em := TEventManager.Create...
em.Obj = anObj;
FreeAndNil(anObj); /// the local anObj variable
if Assigned(em.Obj) then /// this returns true. *that* variable is still assigned
TEventManager = class....
property Obj: TObj; read FObj; read FObj;
procedure BadProc();
var
anObj := TObj;
em: TEventManager;
begin
anObj := TObj.Create();
em := TEventManager.Create...
em.Obj = anObj;
FreeAndNil(anObj); /// the local anObj variable
if Assigned(em.Obj) then /// this returns true. *that* variable is still assigned
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
You are right, when you start having more than one reference of an object, in lists, or other kind of containers, it can be a pain to track the freeing of those objects and make sure that no other reference is remaining.
Why I do is this : For each kind of object, there must be ONE container that is responsible for its deletion, and notify every other components to nil the references.
Constructor TMyObject.Create;
begin
// other construction code ...
RefContainer.Add(Self);
end;
Destructor TMyObject.Destroy;
begin
RefContainer.Delete(Self);
// other freeing code ...
end;
and RefContainer.Delete method is the one that know where to look for references, and implement the notifications that will finally remove all of them. That is just a principle, not a magic solution that works in all cases. That is the price to pay for not having a garbage collector
About the trapping of errors, because as you say even if you have a complex system to reduce these problems, there can be bugs well hidden. So here is how to code the Fun function and solve your hourglass problem. That is what Geert said, but with 2 levels :
Why I do is this : For each kind of object, there must be ONE container that is responsible for its deletion, and notify every other components to nil the references.
Constructor TMyObject.Create;
begin
// other construction code ...
RefContainer.Add(Self);
end;
Destructor TMyObject.Destroy;
begin
RefContainer.Delete(Self);
// other freeing code ...
end;
and RefContainer.Delete method is the one that know where to look for references, and implement the notifications that will finally remove all of them. That is just a principle, not a magic solution that works in all cases. That is the price to pay for not having a garbage collector
About the trapping of errors, because as you say even if you have a complex system to reduce these problems, there can be bugs well hidden. So here is how to code the Fun function and solve your hourglass problem. That is what Geert said, but with 2 levels :
procedure TMyObject.Fun;
begin
Screen.Cursor := crHourGlass;
try
BeginUpdate;
try
// full code, without the EndUpdate raising exception
finally
EndUpdate;
end;
finally
Screen.Cursor := crDefault; // keep this one alone
end;
end;
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
for something like changing the cursor you would actually have to use a stack
pushing and popping the new cursor instead of changing and resetting
sample:
default
user opens form > cursor change to hand (push hand cursor on stack)
user opens query via form > cursor changes to hourglass (push hourglass cursor on stack)
query finished > pop previous cursor = hand
form closes > pop previous cusros = default
pushing and popping the new cursor instead of changing and resetting
sample:
default
user opens form > cursor change to hand (push hand cursor on stack)
user opens query via form > cursor changes to hourglass (push hourglass cursor on stack)
query finished > pop previous cursor = hand
form closes > pop previous cusros = default
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
@Geert_Gruwez
Excellent
Excellent
ASKER
Ok, thanks
>>procedure fun();
should be
procedure TMyObject.fun();
use the try finally structure
procedure TMyObject.Fun;
begin
Screen.Cursor := crHourGlass;
try
Raise Exception.Create('Hourglas
finally
Screen.Cursor := crDefault;
end;
end;