Saturday, April 11, 2009

The Rise and Fall of TObject

"Consider yourself an Delphi expert? Here is a test question for you: Can you name the 8 methods of TObject that are part of the process of constructing and destroying objects in Object Pascal"

That was the introductionary question of my The Rise and Fall of TObject article published in The Delphi Magazine, July 1998. Now we're in the era of Delphi 3, with Delphi 4 just being released.

The question above is in retrospect a trick-question, because there are not 8 but 10 TObject methods related to construction and destroying objects ;). The article only covers the situation in Delphi 3, but Delphi 4 added two more methods (AfterConstruction and BeforeDestruction) to the mix - this was covered in a separate .txt file included with the code.

The updated list of TObject methods involved in construction and destruction is:

  TObject = class
constructor Create;
procedure Free;
class function InitInstance(Instance: Pointer): TObject;
procedure CleanupInstance;
class function InstanceSize: Longint;
procedure AfterConstruction; virtual;
procedure BeforeDestruction; virtual;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
end;

This article was slightly different than most of my other TDM articles (and for that matter, my blog posts) in that it mainly covered basic information that was already available in the documentation. Still, it did dig a little deeper into the implementation details that most other sources.


Here are a couple of excerpts from the article:


"Constructor implementation
Any constructor compiles as if it had been declared something like this:

class function TMyObject.Create(SelfClass: TClass; CalledWithClass: boolean): TMyObject;
var
Self: TMyObject;
begin
if CalledWithClass
then Self := _ClassCreate(SelfClass)
else Self := TObject(SelfClass);

// Actual constructor code goes here

if CalledWithClass then
// Remove exception frame
Result := Self;
end;

The actual compiled code delegates much of the work to a magic System routine called _ClassCreate. This routine is written in assembly, but the logic corresponds to something like this:

function _ClassCreate(aClass: TClass): TObject;
label
ExHandler;
begin
Result := aClass.NewInstance;
Setup special exception frame, exception handler = ExHandler;
Exit;
ExHandler:
except
Result.Free;
raise;
end;
end;

This procedure first creates an object instance by calling the NewInstance class method. Then it manually sets up an exception frame and exception handler. This takes care of calling the Free method and re-raising the exception if something goes wrong in the constructor. Borland probably chose to call the _ClassCreate function instead of compiling the code inline to avoid code bloat."


"The big picture
So now we have a better mental picture of what is going on when objects are created and destroyed. To give you a quick call-diagram of both processes [Updated to show include the Delphi 4 methods - Ed]:

constructor
_CreateClass
NewInstance
GetMem
InitInstance
_AfterConstruction
AfterConstruction

destructor
_BeforeDestruction
BeforeDestruction
_DestroyClass
FreeInstance
CleanupInstance
FreeMem

It easy to see the symmetry between the two operations here. We know that we can override the virtual class methods NewInstance and FreeInstance. This capability can be exploited in many different ways. For example, in Issue 24, Cyril Jandia showed how to use these as a debugging aid in the article “ClassTraps for Delphi and C++ Builder”. In the following section we will see how we can use these methods to take over the actual memory allocation for our object instances."

No comments: