On a more general point, I'd say that whilst referencing "self" as an
interface is an almost unavoidable part of any interface based
framework, the same is not true in the destructor. Anything
referenced or notified during destruction of an object that requires
being passed a reference TO that object, could and most likely should,
have been passed a reference to that object during construction or
during some later operation.
However, if those other objects were holding on to those references,
that would prevent the object from being destroyed in the first place.
During construction an object may interact with another object via its
interface, so that the contained object does not know what exact object
is calling its methods. There is no reason why it shouldn't do that in
its destructor as well.
Another thing that is missing is a safe, common implementation of a
weak reference (an interface reference that does not hold on to --
i.e. does not contribute +1 toward -- the reference count of the
referenced object). You can use an interface reference cast as a
Pointer, but the catch is that the reference must be sure that it will
be notified if the referenced object is destroyed, so that it can NIL
itself. Using a plain Pointer won't do... you have to wrap it inside
some object that can register for those notifications (and be sure
that the object referenced will implement the required notification
for the wrapper to respond to).
This is another scenario which could invoke a call to the container
object's interface.
But, that is not a general purpose solution as it requires that
objects exposing interfaces that may be encapsulated in my weak
reference implementation also implement IOn_Destroy, which is not
built into the VCL. In fact, the VCL doesn't have any such general
purpose system (and neither should it have imho). The
FreeNotification() mechanism is not supported on TObject, and neither
imho should it be.
I certainly wouldn't advocate putting FreeNotification on TObject. It is
far too convoluted. I was thinking more of an external observer, so that
TObject doesn't even know it is being watched. Since TObject's only task
would be to broadcast a message when it was being destroyed, any
observers would be responsible for severing the link.
Not every application -- or indeed every object - needs these things,
nor the overhead that it would incur, adding housekeeping code to the
tear-down of every object.
Most applications simply don't need these sorts of exotica (as
evidenced by the fact that these sort of facilities either don't exist
at all or took so long to be introduced into the core VCL, following
the introduction of interfaces waaaaaay back in Delphi 3 -- Delphi 2
did things slightly differently you may recall).
I started with Delphi 4.
The very beauty of Delphi and the VCL is that we can introduce these
things into the applications (or indeed the small parts of our
applications) that need them, without imposing them on everything else.
When we want the convenience of having everything to hand whether you
want it or not, that's what managed code is for. J
*From:* delphi-boun...@delphi.org.nz
[mailto:delphi-boun...@delphi.org.nz] *On Behalf Of *Todd
*Sent:* Wednesday, 24 November 2010 8:08 p.m.
*To:* NZ Borland Developers Group - Delphi List
*Subject:* Re: [DUG] How's this for inconsistent
Yep -- I remember that my fix was to set the "destructing" state
indicator in a BeforeDestruction() override. This was then tested in
_Release() to render it a NO-OP during execution of the destructor
chain (incomplete, obviously, just to give the idea):
Procedure BeforeDestruction;
SetState(csDestroying);
Function _Release;
If csDestroying in State then EXIT;
Upon reflection, that would only work for a TComponent descendant.
Nothing else needs be done, as long as any further BeforeDestruction
overrides call inherited before doing their work, which they should do
(in my framework I introduced another virtual to be overridden in my
descendants, in case there were occasions when work was done to
generate references during the destructor execution -- even in your
case, the FreeInstance() override is redundant I think, other than as
a sanity/safety check and so could be made subject to some conditional
compilation flag.
*From:* delphi-boun...@delphi.org.nz
<mailto:delphi-boun...@delphi.org.nz>
[mailto:delphi-boun...@delphi.org.nz] *On Behalf Of *Todd
*Sent:* Wednesday, 24 November 2010 6:15 p.m.
*To:* NZ Borland Developers Group - Delphi List
*Subject:* Re: [DUG] How's this for inconsistent
Actually, this would be better
function TamObject._Release: Integer;
begin
Result := InterlockedDecrement(FCount);
if (FCount = 0) then
begin
//add a reference count, incase an interface is acquired and
released during destruction
InterlockedIncrement(FCount);
self.Destroy;
end;
end;
procedure TamObject.FreeInstance;
begin
//remove the reference count added in _Release
InterlockedDecrement(FCount);
assert(FCount = 0,'Destroying object with non-zero reference count');
inherited FreeInstance;
end;
I spotted that they fixed that a while ago -- I remember having to fix
the issue myself many years ago so was quite pleased to see that it
was now taken care of in TInterfaceObject as a matter of course. For
some reason I never noticed the omission of the same facility in the
destructor. And yes, it's a [potentially] big problem.
I need to think about this tho... setting a fake ref count during
execution of the constructor is safe enough as you know precisely when
construction is done and to restore the ref count back to zero.
Setting a fake ref count during destruction strikes me as more
problematic and makes me nervous, but I can't quite put my finger on
why. It might be nothing. That doesn't mean it can't be fixed, only
that the solution put in place for construction might not work for
destruction and it wasn't felt necessary to do any extra work for a
more comprehensive solution.
Certainly in the case of my code where I fixed this I had specific
"constructing" / "destructing" state markers (it wasn't a general
purpose interfacedobject class but a base class in a far richer
framework that happened to also implement its own version of IUnknown)
-- I know I didn't rely on side effects of a faked ref count.
*From:* delphi-boun...@delphi.org.nz
<mailto:delphi-boun...@delphi.org.nz>
[mailto:delphi-boun...@delphi.org.nz] *On Behalf Of *Todd
*Sent:* Wednesday, 24 November 2010 16:55
*To:* NZ Borland Developers Group - Delphi List
*Subject:* [DUG] How's this for inconsistent
The Delphi developer who implemented TInterfacedObject obviously
considered the case when an interface reference is grabbed during
construction......
// Set an implicit refcount so that refcounting
// during construction won't destroy the object.
class function TInterfacedObject.NewInstance: TObject;
begin
Result := inherited NewInstance;
TInterfacedObject(Result).FRefCount := 1;
end;
procedure TInterfacedObject.AfterConstruction;
begin
// Release the constructor's implicit refcount
InterlockedDecrement(FRefCount);
end;
but didn't consider applying the same logic during destruction. So
grabing an interface reference during destruction causes all hell to
break loose, as the _Release method tries to free the object again and
again recursively.
procedure TInterfacedObject.BeforeDestruction;
begin
if RefCount <> 0 then
Error(reInvalidPtr);
end;
function TInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
Todd.
_______________________________________________
NZ Borland Developers Group - Delphi mailing list
Post:delphi@delphi.org.nz <mailto:delphi@delphi.org.nz>
Admin:http://delphi.org.nz/mailman/listinfo/delphi
Unsubscribe: send an email todelphi-requ...@delphi.org.nz
<mailto:delphi-requ...@delphi.org.nz> with Subject: unsubscribe
_______________________________________________
NZ Borland Developers Group - Delphi mailing list
Post:delphi@delphi.org.nz <mailto:delphi@delphi.org.nz>
Admin:http://delphi.org.nz/mailman/listinfo/delphi
Unsubscribe: send an email todelphi-requ...@delphi.org.nz
<mailto:delphi-requ...@delphi.org.nz> with Subject: unsubscribe
_______________________________________________
NZ Borland Developers Group - Delphi mailing list
Post: delphi@delphi.org.nz
Admin: http://delphi.org.nz/mailman/listinfo/delphi
Unsubscribe: send an email to delphi-requ...@delphi.org.nz with Subject:
unsubscribe
_______________________________________________
NZ Borland Developers Group - Delphi mailing list
Post: delphi@delphi.org.nz
Admin: http://delphi.org.nz/mailman/listinfo/delphi
Unsubscribe: send an email to delphi-requ...@delphi.org.nz with Subject:
unsubscribe