Discussion:
Nil after freeing object then using Assigned function STILL returns access violation error?
(too old to reply)
Jessica Loriena
2004-09-16 16:52:24 UTC
Permalink
Hi.. I hope someone will help be with a strange problem I'm having:

I create tab sheets dynamically and attach a form to it at runtime
like this:

***************************************************************
MainForm:
***************************************************************
public
{ Public declarations }
MyTabSheet: TcxTabSheet; (a DevExpress Tab Sheet Component)


procedure TMainForm.actionNewTabSheet(Sender: TObject);
begin
// MyTabSheet always set to nil after freed see below)
if not Assigned(MyTabSheet) then
begin
MyTabSheet := TcxTabSheet.Create(Application);
MyTabSheet.PageControl := MyPageControl;
MyTabSheet.Caption := 'Sheet One';
MyTabSheet.Show;

MyForm := MyForm.Create(Application);
MyForm.Parent := MyTabSheet;
MyForm.Show;
end
else
MyPageControl.ActivePageIndex := MyTabSheet.PageIndex;
end;
---------------------------------------------------------------

As you can see, if MyTabSheet is already Assigned (created), I just
make it the active sheet instead of creating a new one.

In MyForm (contained in MyTabSheet), I have an OnClose event like
this:

***************************************************************
MyForm
***************************************************************
procedure TMyForm.FormClose(Sender: TObject; var Action:
TCloseAction);
begin
Release;
MainForm.MyTabSheet.Free;
MainForm.MyTabSheet := nil;
end;
---------------------------------------------------------------

The strange thing here is that this code works, but SOMETIMES (NOT
rarely), an Access Violation error comes up after closing the tabbed
form (which frees the tab sheet). After the error message, the sheet
closes normally and execution continues as normal.

What's wrong with the code?? I searched for many hours and still can't
find where a logical error might be. The error message is really
annoying.

I'd very much appreciate your help.

Thanks!
Jamie
2004-09-16 21:14:50 UTC
Permalink
don't use the "release".
if your trying to free your form when it
closes then use the Action property.
Action := caFree;
etc..
actually, if you were to use the form at the owner of your
created sheets you wouldn't need to do that.., the freeing of the
form will enumerate down through the list of owned objects and free them
before it free's it self.
Post by Jessica Loriena
I create tab sheets dynamically and attach a form to it at runtime
***************************************************************
***************************************************************
public
{ Public declarations }
MyTabSheet: TcxTabSheet; (a DevExpress Tab Sheet Component)
procedure TMainForm.actionNewTabSheet(Sender: TObject);
begin
// MyTabSheet always set to nil after freed see below)
if not Assigned(MyTabSheet) then
begin
MyTabSheet := TcxTabSheet.Create(Application);
MyTabSheet.PageControl := MyPageControl;
MyTabSheet.Caption := 'Sheet One';
MyTabSheet.Show;
MyForm := MyForm.Create(Application);
MyForm.Parent := MyTabSheet;
MyForm.Show;
end
else
MyPageControl.ActivePageIndex := MyTabSheet.PageIndex;
end;
---------------------------------------------------------------
As you can see, if MyTabSheet is already Assigned (created), I just
make it the active sheet instead of creating a new one.
In MyForm (contained in MyTabSheet), I have an OnClose event like
***************************************************************
MyForm
***************************************************************
TCloseAction);
begin
Release;
MainForm.MyTabSheet.Free;
MainForm.MyTabSheet := nil;
end;
---------------------------------------------------------------
The strange thing here is that this code works, but SOMETIMES (NOT
rarely), an Access Violation error comes up after closing the tabbed
form (which frees the tab sheet). After the error message, the sheet
closes normally and execution continues as normal.
What's wrong with the code?? I searched for many hours and still can't
find where a logical error might be. The error message is really
annoying.
I'd very much appreciate your help.
Thanks!
Jessica Loriena
2004-09-17 04:58:11 UTC
Permalink
I'm not sure I understood your reply correctly, but I removed the
"Release" command from TMyForm.FormClose, but the Access Violation
message still appears oftenly. I also tried Action := caFree, but in
this case, there's an Access Violation message everytime I close the
form.

In case I misunderstood something, please correct me. Otherwise, I
guess I still need a solution..

I would greatly appreciate any help.

TIA
Post by Jamie
don't use the "release".
if your trying to free your form when it
closes then use the Action property.
Action := caFree;
etc..
actually, if you were to use the form at the owner of your
created sheets you wouldn't need to do that.., the freeing of the
form will enumerate down through the list of owned objects and free them
before it free's it self.
Post by Jessica Loriena
I create tab sheets dynamically and attach a form to it at runtime
***************************************************************
***************************************************************
public
{ Public declarations }
MyTabSheet: TcxTabSheet; (a DevExpress Tab Sheet Component)
procedure TMainForm.actionNewTabSheet(Sender: TObject);
begin
// MyTabSheet always set to nil after freed see below)
if not Assigned(MyTabSheet) then
begin
MyTabSheet := TcxTabSheet.Create(Application);
MyTabSheet.PageControl := MyPageControl;
MyTabSheet.Caption := 'Sheet One';
MyTabSheet.Show;
MyForm := MyForm.Create(Application);
MyForm.Parent := MyTabSheet;
MyForm.Show;
end
else
MyPageControl.ActivePageIndex := MyTabSheet.PageIndex;
end;
---------------------------------------------------------------
As you can see, if MyTabSheet is already Assigned (created), I just
make it the active sheet instead of creating a new one.
In MyForm (contained in MyTabSheet), I have an OnClose event like
***************************************************************
MyForm
***************************************************************
TCloseAction);
begin
Release;
MainForm.MyTabSheet.Free;
MainForm.MyTabSheet := nil;
end;
---------------------------------------------------------------
The strange thing here is that this code works, but SOMETIMES (NOT
rarely), an Access Violation error comes up after closing the tabbed
form (which frees the tab sheet). After the error message, the sheet
closes normally and execution continues as normal.
What's wrong with the code?? I searched for many hours and still can't
find where a logical error might be. The error message is really
annoying.
I'd very much appreciate your help.
Thanks!
AlanGLLoyd
2004-09-16 19:38:23 UTC
Permalink
Post by Jessica Loriena
What's wrong with the code?? I searched for many hours and still can't
find where a logical error might be. The error message is really
annoying.
Why are you allocating an owner to the tabsheet when you create it, and then
freeing it yourself. Although you allocate nil to the tabsheet when you free
it, this might be a source of your problem. Personally I would allocate nil to
the owner and then free it myself if one _has_ to.

Freeing the parent of a form in an event of that form sounds a little like
sawing off the branch you are sitting on, too. I'd feel a little twitchy about
that too.

Why would you be creating and freeing the tab sheet anyway, why not create it
and then hide the tab when you don't want to display it.

Complexity always add traps <g>.

Alan Lloyd
***@aol.com
Justus J.
2004-09-16 20:30:57 UTC
Permalink
On 16 Sep 2004 19:38:23 GMT, ***@aol.com (AlanGLLoyd) wrote:

...
Post by AlanGLLoyd
Freeing the parent of a form in an event of that form sounds a little like
sawing off the branch you are sitting on, too. I'd feel a little twitchy about
that too.
...

Does that mean you should NEVER free a procedurally created object
with Self as the owner, and always let Delphi clean it up?

I use code like this a lot (MDI application):

procedure frmForm.Create(Sender: TObject);
begin
oTest := TTest.Create(Self);
end;

procedure frmForm.FormCloseQuery(Sender: TObject; var CanClose:
Boolean);
begin
if Assigned(oTest) then
begin
oTest.Free;
oTest := Nil;
end;
end;

and DO have sometimes strange crashes at Nil objects. Quite difficult
to debug, anyway.

BTW, i thougt freeing an instance sets it to nil, so the last
statement in :
oTest.Free;
oTest := Nil;
is useless. Is that true?

Justus
AlanGLLoyd
2004-09-16 20:44:25 UTC
Permalink
Post by Justus J.
BTW, i thougt freeing an instance sets it to nil,
No, use FreeAndNil in later Delphi to do that.

The Owner is responsible for freeing the memory of an object. Because of timing
etc I would always like to have a nil owner for something I had to free myself.
But allocating nil to the object reference should always make it OK.

If one relies on nil-checks in a program, and because there may be threads,
then I have seen the following advocated ...

var
TempMyObj : TMyObj;
begin
TempMyObj := MyObj; // assign object ref to a local variable
MyObj := nil; // set object ref to nil, then any programmatic checks on the
object produce nil
TempMyObj.Destroy; // destroy the object

Then there is never any access to a free'd object.

Alan Lloyd
***@aol.com
Maarten Wiltink
2004-09-17 10:09:33 UTC
Permalink
Post by AlanGLLoyd
Post by Justus J.
BTW, i thougt freeing an instance sets it to nil,
No, use FreeAndNil in later Delphi to do that.
The Owner is responsible for freeing the memory of an object. Because
of timing etc I would always like to have a nil owner for something I
had to free myself. But allocating nil to the object reference should
always make it OK.
But the TComponent.Owner mechanism involves complete registration and
deregistration on Create/Destroy with the stated Owner, so it doesn't
really matter.

I've never seen problems with it, but I too always use a nil Owner.
That avoids the possible problem of having the Owner and then the _owner_
of the object trying to free it. (In a good design, the Owner should be
the owner, too, and whatever means you've set up to free owned objects
should be hooked from the owner's destructor. So your own logic should
still preempt TComponent's mechanisms.)

Freeing an object _can't_ set all references to it to nil. There may be
aliases. Exactly one reference should be the "owning" reference (note
that Owner is also a reference, and it always thinks it's the owning
one), but the run-time system can't distinguish between them. That's,
necessarily, up to the application.

As a side note, in a garbage-collected system, all references are
owning except that nobody gets to free the object. The run-time system
no longer needs to distinguish between references, so now it can do the
reverse of clearing all references when the object is destroyed: it can
destroy the object when all references are cleared.

As another side note, setting a reference to nil may not be the right
thing to do. It may be in a list that expects to hold only valid
pointers. It may be a property value, with behaviour hooked to value
changes - most especially destroying itself when its parent dies. This
complicates things beyond the simple clearing of four bytes in memory.
Post by AlanGLLoyd
If one relies on nil-checks in a program, and because there may be
threads, then I have seen the following advocated ...
var
TempMyObj : TMyObj;
begin
TempMyObj := MyObj;
MyObj := nil;
TempMyObj.Destroy;
Then there is never any access to a free'd object.
If I am not mistaken, this reads suspiciously like the body of
FreeAndNil (know thy enemies...).

But it's not thread-safe. Try running two instances of this very
code fragment side by side, leapfrogging one statement at a time.
(This is the canonical way of finding race conditions.)

The standard "X.Free; X:=nil;" code is a critical section in its
entirety; in the above code, the first two statements are. There
is no way to beat the fact that disowning the owning reference
and clearing it needs to be done atomically... _if_ threads are
involved.

Groetjes,
Maarten Wiltink

Loading...