[DUG] A change in upgrade policy coming from Embarcadero

Jolyon Smith jsmith at deltics.co.nz
Fri Sep 18 19:36:20 NZST 2009


It's not knowing what objects that have been created that's the problem,
it's knowing what *references* to those objects might be out there and
relied on.

Without a framework/runtime enforced mechanism to declare and maintain such
references and/or rigorous discipline on the part of the application
developer you're on a hiding to nothing.

That is, partly at least, what the "managed" part of "managed" code gives
you - that required level of oversight and control, preventing you from
doing things like storing a reference to an object in an integer just
because it happens to be convenient or chucking a reference "over the fence"
somewhere out of reach of the oversight mechanics.


But really, if you want garbage collection you can have it today, in Delphi.
Yes it involves reference counting, yes it involves interfaces and some
people have a HUGE downer on reference counting for some reason (I suspect
mostly because it's seen as "old hat").  But it's the foundation of COM and,
y'know, COM did pretty well off the back of that form of garbage collection
and some pretty important systems in turn rest and rely on that technology.

It's also interesting to note that more COM API's were added to Windows 7
than .NET ones, for example.

Now, for sure it's not perfect, but neither is the generational garbage
collector in .NET and like any technique, if used carefully and when
appropriate it gets the job done very effectively.

A key aspect of reference counted lifetime management (in Delphi at least)
is that you retain determinism in that lifetime management so you do not
have to worry about "finalisation" or "disposal" as a separate concern from
"deallocation".

You continue taking care of finalisation in your destructors as you always
have done.


I'll show you some code that I worked with very successfully that took all
the work of managing sql objects away using a factory that returns query
interface references:

    Var
      Qry: ISQLQuery;
    Begin
      Qry := SQLFactory.Query('select blah blah blah');
      Qry.Parameter['ID'].AsInteger := aID;
      Qry.Open;
        ...
       etc
    End;

No need to worry about try..finally Free or try..finally anything else for
that matter.  When the qry variable goes out of scope its ref count hits 0
and it is destroyed, cleaning up as required when it does so.


In fact, that's not strictly true in this case.  It's actually better even
than that.


The SQLFactory is actually internally maintaining a cache of query objects.
When asked for a query, it efficiently determines whether there is an unused
query object in the cache with that same SQL.  If there is, you get a
reference to that (unless it is already in use - you don't want to go
setting parameters and re-executing a query that some other code is
currently FETCHING from, for example).

If no object exists with that SQL then a new cached object is created and a
reference to that is returned.

If an object exists for the SQL but is already in use (easily determined by
the fact that ref count > 0) then a new, *UN*cached object is created and
reference to THAT is returned.

When the ref count hits zero on a query object, what it does depends on
whether it is cached or not but in either case the destructor is called as
normal.

However, the FreeInstance method (which actually takes cares of returning
memory to the system) is overridden.  In this override, the object does two
different things depending on whether it is cached or not.

If NOT cached, it simply calls inherited - this results in the object being
freed as normal.

If cached however, it resets the ref count to 0 (zero) but does NOT call
inherited, so the object is not Free'd (but it's destructor *has* still been
called).


The "client" code is blissfully ignorant of all these machinations.  All you
have to do to safely work with these objects with such sophisticated
management behind the scenes is get them from the factory when needed.

That's it.

And all this in code that could compile in Delphi 3, since that was when
interfaces as we know them today were introduced, if it really had to (which
it doesn't of course), with no need to jump through hoops or impose awkward
syntax incantations on the client code.

Just use the technology we have had for years and understand well.




More information about the Delphi mailing list