Here's a text I wrote out of sheer frustration that gives a little more insight in what interfaces are and how to use them.
There is still some work to do: GUIDS, Typelibraries, CORBA's ORB-like model, Async etc, but here you go, it is not
complete yet, but covers the basics.
-----------------------------------------
Interface support in modern object pascal
Reference counted vs non-reference counted interfaces
-----------------------------------------
Modern Object Pascal supports both COM and CORBA interfaces,
but actually there is a third type of interface that is native to
Object Pascal. There are many misunderstandings about interfaces so
let's try and categorise them by traits.
The most common misconception is that you can distinguish a COM
interface from a CORBA interface because the first is reference counted
and the second is not. Let me put you out of your dreams: that is wrong!
Both COM and CORBA interfaces can be implemented without reference count.
Both COM and CORBA interfaces can be implemented with reference count.
This is to a large extend implementation detail and not a part of core
specifications, e.g. with a little thought you can implement AddRef and Release on a CORBA
interface!
From the programmers' manual:
"The {$INTERFACES} directive tells the compiler what it should take as the parent interface of an
interface declaration which does not explicitly specify a parent interface. By default the Windows
COM IUnknown interface is used. Other implementations of interfaces (CORBA or Java) do not
necessarily have this interface, and for such cases, this directive can be used."
So where does that misunderstanding come from?
Well, actually, you may not know that but is is to a certain extend
rather Object Pascal specific: Delphi implemented COM interfaces with
a default implementation that used the reference counted part of the COM
specification by default. So almost everybody that uses COM interfaces
in Object Pascal started to see COM interfaces in that light: reference
counted. And Delphi implemented CORBA interfaces without reference counting.
Let me explicitly state that was also the common practice in both camps,
it just isn't the whole of the story.
Here we introduce the third type of interface that Object Pascal supports:
An interface type that is neither COM nor CORBA, loosly based on COM and
local to an application. It does not expose an interface to the outside
world. It does not rely on extra software or OS level support.
To make myself clear: All these three types of interface can support
reference counting or not!
So what are the most important traits of the different types of interfaces
in Object Pascal from a programmers point of view?
Well, by no means complete, but for this purpose I define them like this:
1. We have interfaces that expose themselves to the outside world
2. We have interfaces that are local to a process or application.
3. We have interfaces that are reference counted
4. We have interfaces that are not reference counted.
ad 1. requires a mechanism to talk to other processes.
In COM that is the typelibrary. (META-language, inhibited)
In Corba that is the interface repository. (ORB-like, centralized)
ad 2. does not need 1. but may implement it anyway, often by accident
or lack of knowledge.
ad 3. the most important trait of reference counted interfaces is that it
allows for automatic memory management provided correcty used.
ad 4. non-reference counted interfaces have no memory management support
by themselves.
For the purpose of its use, let's explain how to work with reference
counted interfaces versus non-reference counted interfaces.
----------------------------
Reference counted interfaces
----------------------------
Provided you use them properly, reference counted interfaces are a bit
like strings: when an object supports a reference counted interface
it will be automatically destroyed when the reference count is zero.
But this is ONLY the case when you use them properly:
1. An object that supports a reference counted interface needs to be
instatiated through an interface variable instead of an object variable
for automatic memory management to work. If automatic memory management
of an object is your goal, never instantiate such an object through
an object or class variable.
Example:
{$INTERFACES COM} // only partially true!
type
TMyobject = class(TinterfacedObject);
// WRONG: memory leak, no automatic memory management occurs.
var
Myobject:TmyObject;
begin
MyObject := TMyObject.Create;
// If you relied on automatic management, well, you have a leak!
end.
// Better, but still wrong:
var
Myobject:TmyObject;
begin
MyObject := TMyObject.Create;
try
...
finally
MyObject.Free; //Yup, you do this by hand. Bit silly
end;
end.
So what's wrong in the second example?
Well, you have to free by hand. It is easy to forget that reference counting
does not work when you instantiate through an object variable. This is one
of the most common causes of memory leaks in an application that uses reference
counted interfaces. DON'T DO THAT! Keep that for classes and objects that do not
support a reference counted interface.
// RIGHT, PROPER, GOOD, EXCELLENT, YOU GOT IT
var
Myobject:IInterface; // instantiate through interface variable
begin
MyObject := TMyObject.Create as IInterface;
// Guaranteed automatic memory management, no leaks!
// No try/finally, nothing
end.
So never ever forget that if you use a reference counted interface type
you MUST instantiate through an interface variable. NEVER through a
class variable. Then you mix things up and you are asking for trouble.
Pay special attention to that when you use them in your code.
COM interfaces and COM-like native Object Pascal interfaces in default
mode support reference counting by default. If you want to use any of
these two types non-reference counted you have to do a little work: you will need to override
the implementation of AddRef and Release:
type
TMyObject = class(TInterfacedObject)
function _Addref:Integer;stdcall;override;
function _Release:integer;stdcall;override;
end;
function TMyObject._AddRef:integer;
begin
//Keep refcount on 1
Result := 1;
end;
function TMyObject. _Release:integer;
begin
Result := 1;
end;
A CORBA interface does not have a refcount mechanism by default, but you can
implement it! Here's part of it. (You actually have to do a bit more, like operator support for this to work)
{$INTERFACES CORBA}
type
IRefcountedCorbaInterface = interface
function _AddRef:integer;
function _Release:integer;
end;
TMyRefcountedCorbaObject = class(Tobject, IRefcountedCorbaInterface)
private
fRefcount:integer;
public
function _Addref:integer;virtual;
function _Release:integer;virtual;
end;
As you can see there really is only the difference between refcounted or not,
the difference between CORBA and COM can be made to disappear.
There's lot's more to learn about interfaces, like type libraries and
dispatch handling but that is for a different chapter.
I hope you have learned that:
1. You have refcounted and non-refcounted interfaces. COM and CORBA
can implement both.
2. A refcounted interfaced object should be instantiated through an
interface variable.
3. A non-refcounted interfaced object should be instantiated through
an object (class) variable.
4. Never ever mix-up interface and class variables on the same object!
That is a recipe for disaster.
5. Object Pascal implements COM and CORBA like interfaces on ALL
platforms.
Next time Is and As and QueryIntterface.