Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Quick Interface Question: Delphi Win32

8 views
Skip to first unread message

Lee Jenkins

unread,
Jun 23, 2008, 5:13:01 PM6/23/08
to

Hi all,

I'm seriously beginning to look at using reference counted interfaces in Delphi
win32. I've already started to use non-reference counted interfaces heavily
since I started seriously progressing towards design patterned motivated OO
programming about 6 months ago.

I'm kind of jealous of Java and .Net which has GC and how not having to track
and free my objects seems to lend itself to better OO programming with patterns
like factory and builder.

I've heard again and again that you must be careful when mixing interfaces with
standard object types in code. Can anyone explain the pitfalls or point me to
some good reading on the subject?

--

Warm Regards,

Lee

Charles McAllister

unread,
Jun 23, 2008, 6:09:01 PM6/23/08
to

this might be helpful for you...
http://qc.borland.com/wc/qcmain.aspx?d=9157

i have a fair number of objects which are not reference counted and so i must be careful that when acquiring an interface from such an object, that i always do so in a method which is separate from where the object is destroyed. (see workaround #2 in bug report).

most of my classes are reference counted, so the issue is not as important to me as it used to be.
hope this helps!

Marc Rohloff [TeamB]

unread,
Jun 24, 2008, 9:12:08 AM6/24/08
to
On Mon, 23 Jun 2008 17:13:01 -0400, Lee Jenkins wrote:

> I've heard again and again that you must be careful when mixing interfaces with
> standard object types in code. Can anyone explain the pitfalls or point me to
> some good reading on the subject?

Generally the pitfall here is when an interface reference is nilled
(often implicitly) after you have free'd your object. For ex:

var o:TInterfacedObject; i:IInterface;
begin
o := TInterfacedObject.Create;
i := o;
//
o.Free;
end; {The compiler implicitly performs an i := nil at this point}

This code would generally cause an AV at the 'end' statement, and
unfortunately it is usually harder to trace than this. I usually stick
to reference counted interfaces. If I want to use direct class
references then I call AddRef when the object is created (or the
reference is copied) and I call Release instead of Free (or when the
reference goes out of scope)

i.e.
var o:TInterfacedObject; i:IInterface;
begin
o := TInterfacedObject.Create;
o._AddRef;
i := o;
//
o._Release;
end;

--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com

Wayne Niddery (TeamB)

unread,
Jun 24, 2008, 1:19:20 PM6/24/08
to
"Lee Jenkins" <l...@nospam.net> wrote in message
news:486011dd$1...@newsgroups.borland.com...

>
> I've heard again and again that you must be careful when mixing interfaces
> with standard object types in code. Can anyone explain the pitfalls or
> point me to some good reading on the subject?


The major issue to watch for is to make sure you only use interface
variables when dealing with reference-counted interfaces, do not use object
variables:

var
foo: TMyInterfacedClass;
begin
foo := TMyInterfacedClass.Create; // ref count still 0
PassToProcExpectingInterface(foo); // ref count incremented
// ref count now decremented back to zero on return from proc, and it is
released
foo.DoSomething; // kaboom
end;

Same code but using an interface type variable:

var
foo: IMyInterface;
begin
foo := TMyInterfacedClass.Create; // ref count correctly set to 1
PassToProcExpectingInterface(foo); // ref count incremented
// ref count now decremented back to one on return from proc
foo.DoSomething; // everything is fine
end; // foo released on procedure exit since ref count goes to 0

--
Wayne Niddery - TeamB (www.teamb.com)
Winwright, Inc. (www.winwright.ca)

Joao Morais

unread,
Jun 24, 2008, 9:28:16 PM6/24/08
to
Lee Jenkins wrote:
> I'm kind of jealous of Java and .Net which has GC and how not having to
> track and free my objects seems to lend itself to better OO programming
> with patterns like factory and builder.

Another issue, but regarding refcounting mechanism (interface or not) is
that you need to take care with circular references, eg:

TIntfObj1 = class(TBase, IIntf1)
Intf2: IIntf2;

TIntfObj2 = class(TBase, IIntf2)
Intf1: IIntf1;

Iow an object which points to another object which ... points to the
first one, incrementing their refcounts. All of them, and of course all
objects referenced or owned by them, will remain in memory. A problem
that btw doesn't exist with GC.

Create one Pointer reference (weak reference, ie, don't inc refcount) in
order to break the chain and solve this issue.

Joao Morais

Charles McAllister

unread,
Jun 25, 2008, 12:40:56 AM6/25/08
to

Wayne Niddery (TeamB) wrote:
> var
> foo: TMyInterfacedClass;
> begin
> foo := TMyInterfacedClass.Create; // ref count still 0
> PassToProcExpectingInterface(foo); // ref count incremented
> // ref count now decremented back to zero on return from proc, and it
> is released
> foo.DoSomething; // kaboom
> end;
>

if PassToProcExpectingInterface uses a const param for the interface i don't think the refcount gets altered.
this is why i always use const for interface parameters -- as a sort of "just in case"

Lee Jenkins

unread,
Jun 25, 2008, 8:48:20 AM6/25/08
to

Neat stuff, all. Thanks to all for your responses.

--

Warm Regards,

Lee

Charles McAllister

unread,
Jun 25, 2008, 2:04:49 PM6/25/08
to
Joao Morais wrote:
> Iow an object which points to another object which ... points to the
> first one, incrementing their refcounts. All of them, and of course all
> objects referenced or owned by them, will remain in memory. A problem
> that btw doesn't exist with GC.
>
> Create one Pointer reference (weak reference, ie, don't inc refcount) in
> order to break the chain and solve this issue.
>
> Joao Morais

I've seen this done before but I'm not a big fan of this technique.
For instance, if an interface is destroyed, then your weak reference becomes stale which is a dangerous situation to be in. So instead of allowing the weak reference to become stale, what you would do is create a method like InterfaceDestroyed or something like that so that the weak reference can be nilled out at the appropriate time. But since you're having to create this notification method anyway, you might as well not use a weak reference at all, and call your notification method, ReleaseReference, which will nil out the duplicate interface and allow the other instance to be destroyed.

Joao Morais

unread,
Jun 25, 2008, 2:46:22 PM6/25/08
to
Charles McAllister wrote:
> Joao Morais wrote:
>> Iow an object which points to another object which ... points to the
>> first one, incrementing their refcounts. All of them, and of course
>> all objects referenced or owned by them, will remain in memory. A
>> problem that btw doesn't exist with GC.
>>
>> Create one Pointer reference (weak reference, ie, don't inc refcount)
>> in order to break the chain and solve this issue.
>
> I've seen this done before but I'm not a big fan of this technique.
> For instance, if an interface is destroyed, then your weak reference
> becomes stale which is a dangerous situation to be in. So instead of
> allowing the weak reference to become stale, what you would do is create
> a method like InterfaceDestroyed or something like that so that the weak
> reference can be nilled out at the appropriate time. But since you're
> having to create this notification method anyway, you might as well not
> use a weak reference at all, and call your notification method,
> ReleaseReference, which will nil out the duplicate interface and allow
> the other instance to be destroyed.

The problem is: when should you call InterfaceDestroyed? You don't have
a simple or obvious way to do it.

Otoh a weak reference isn't that bad, the pointer will become invalid
only if the instance that it points reach its refcount to zero, meaning
that itself, the pointer, will be destroyed as well.

Joao Morais

Marc Rohloff [TeamB]

unread,
Jun 25, 2008, 3:03:48 PM6/25/08
to
On Wed, 25 Jun 2008 13:04:49 -0500, Charles McAllister wrote:

> I've seen this done before but I'm not a big fan of this technique.

> For instance ...

Most circular references I see are where one object owns another and
the child needs to know who its parent is. In this case the child
usually keeps a weak reference to its parent and it is not a problem
since when the parent is destroyed the child is too.

Another common practice in this situation is to forward all reference
counting to the parent. i.e. If someone copies a reference to the
child the RefCount on the parent is actually incremented. This
prevents the parent from being destroyed while the child is still
being used.

Charles McAllister

unread,
Jun 25, 2008, 3:10:25 PM6/25/08
to
Joao Morais wrote:
> The problem is: when should you call InterfaceDestroyed? You don't have
> a simple or obvious way to do it.
>

It depends on the situation, but to give an example, suppose TFirstObject and TSecondObject both keep a reference to a IMyInterface instance. So they should both have a way to clear their reference like so...

destructor TFirstObject.Destroy;
begin
// FSecondObject has a copy of FMyInterface so notify...
FSecondObject.ReleaseReference;

// not required to do this just giving an example
FMyInterface := nil;

// at this point memory is freed, and no stale references

inherited;
end;

procedure TFirstObject.ReleaseReference;
begin
FMyInterface := nil;
end;

in this example TFirstObject "knows" that TSecondObject needs notification and vice versa. In situations where such knowledge is not explicitly shared, you can solve this using an observer pattern or simple event.

> Otoh a weak reference isn't that bad, the pointer will become invalid
> only if the instance that it points reach its refcount to zero, meaning
> that itself, the pointer, will be destroyed as well.
>

You're right the pointer will be destroyed, but the problem is not a memory leak.
Since the pointer is never explicitly set to nil, at some point there may be code that assumes the pointer is still valid e.g.,

procedure TSecondObject.Whatever;
begin
...
// do something that inadvertently destroys TFirstObject
...
// error happens here (if you're lucky)
IMyInterface(FPointer).DoSomething;
...
end;

now you might think all you have to do is check to see if FPointer is valid like so...

if Assigned(FPointer) then
IMyInterface(FPointer).DoSomething;

...however this will not work to fix the error as FPointer is not nil.

Charles McAllister

unread,
Jun 25, 2008, 3:16:43 PM6/25/08
to
Marc Rohloff [TeamB] wrote:
> Most circular references I see are where one object owns another and
> the child needs to know who its parent is. In this case the child
> usually keeps a weak reference to its parent and it is not a problem
> since when the parent is destroyed the child is too.
>

I know there's lots of code out there that runs great using weak references.
For me personally, I don't ever allow stale references to exist, unless there is a specific reason to do so, for example regarding a performance bottleneck.

> Another common practice in this situation is to forward all reference
> counting to the parent. i.e. If someone copies a reference to the
> child the RefCount on the parent is actually incremented. This
> prevents the parent from being destroyed while the child is still
> being used.
>

Interesting idea!

Joao Morais

unread,
Jun 25, 2008, 3:36:38 PM6/25/08
to
Charles McAllister wrote:
> Joao Morais wrote:
>> The problem is: when should you call InterfaceDestroyed? You don't
>> have a simple or obvious way to do it.
>
> It depends on the situation, but to give an example, suppose
> TFirstObject and TSecondObject both keep a reference to a IMyInterface
> instance.

If I really took your point... This doesn't represent a circular
reference scenario, so you don't need to do anything, all references
will be destroyed. Otherwise...

> So they should both have a way to clear their reference like
> so...
>
> destructor TFirstObject.Destroy;
> begin
> // FSecondObject has a copy of FMyInterface so notify...
> FSecondObject.ReleaseReference;

... if this would represent a circular reference scenario, the
destructor wouldn't be called, and so the ReleaseReference.

>> Otoh a weak reference isn't that bad, the pointer will become invalid
>> only if the instance that it points reach its refcount to zero,
>> meaning that itself, the pointer, will be destroyed as well.
>
> You're right the pointer will be destroyed, but the problem is not a
> memory leak.
> Since the pointer is never explicitly set to nil, at some point there
> may be code that assumes the pointer is still valid e.g.,
>
> procedure TSecondObject.Whatever;
> begin
> ...
> // do something that inadvertently destroys TFirstObject

You are right. This wouldn't happen... except if another object which
doesn't belong to the 'chain' references the 'weak' object (with a weak
reference pointer). All objects of the chain will be destroyed and the
pointer will reference an invalid memory area.

Otherwise, if such object is only referenced by object(s) that belongs
to the chain (Marc's sample is a good one), this issue will never happen.

> // error happens here (if you're lucky)
> IMyInterface(FPointer).DoSomething;
> ...
> end;
>
> now you might think all you have to do is check to see if FPointer is
> valid like so...
>
> if Assigned(FPointer) then
> IMyInterface(FPointer).DoSomething;
> ...however this will not work to fix the error as FPointer is not nil.

No no, I wouldn't say that.

Joao Morais

Joao Morais

unread,
Jun 25, 2008, 3:51:22 PM6/25/08
to
Charles McAllister wrote:
> Joao Morais wrote:
>>> It depends on the situation, but to give an example, suppose
>>> TFirstObject and TSecondObject both keep a reference to a
>>> IMyInterface instance.
>>
>> If I really took your point... This doesn't represent a circular
>> reference scenario, so you don't need to do anything, all references
>> will be destroyed. Otherwise...
>
> I should say "TFirstObject and TSecondObject both keep a reference to
> the SAME IMyInterface instance.". In other words
> TFirstObject.FMyInterface = TSecondObject.FMyInterface
> TFirstObject and TSecondObject don't necessarily implement any interfaces.

This would only represent a circular reference if some IMyInterface
instance points to FirstObj or SecondObj, otherwise you won't have mem leak.

Joao Morais

Charles McAllister

unread,
Jun 25, 2008, 3:45:32 PM6/25/08
to
Joao Morais wrote:
>> It depends on the situation, but to give an example, suppose
>> TFirstObject and TSecondObject both keep a reference to a IMyInterface
>> instance.
>
> If I really took your point... This doesn't represent a circular
> reference scenario, so you don't need to do anything, all references
> will be destroyed. Otherwise...
>

I should say "TFirstObject and TSecondObject both keep a reference to the SAME IMyInterface

Charles McAllister

unread,
Jun 25, 2008, 4:10:45 PM6/25/08
to
Joao Morais wrote:
>> I should say "TFirstObject and TSecondObject both keep a reference to
>> the SAME IMyInterface instance.". In other words
>> TFirstObject.FMyInterface = TSecondObject.FMyInterface
>> TFirstObject and TSecondObject don't necessarily implement any
>> interfaces.
>
> This would only represent a circular reference if some IMyInterface
> instance points to FirstObj or SecondObj, otherwise you won't have mem
> leak.
>
> Joao Morais

Yes you're right, I see now. I need a better example.

David Novo

unread,
Jul 3, 2008, 10:00:59 AM7/3/08
to
Hello,

One thing I do to help with the problem below is that my ancestor
business object checks its refcount when it is destroyed. If it is not
zero, it raises a message. That way, I can trap exactly when an object
is being freed where there are still outstanding interfaces. If you
wait until the A/V hits, by the interface variable going out of scope,
its often very difficult to find where the underlying object was freed.

Note: FASTMM helps a lot with this because it can report if you called
methods on freed objects and the call stack when that object was freed,
if you have the correct debugging compiler defines set in FASTMM.


Marc Rohloff [TeamB] wrote:

> Generally the pitfall here is when an interface reference is nilled
> (often implicitly) after you have free'd your object. For ex:
>

--

David Novo

unread,
Jul 3, 2008, 10:37:12 AM7/3/08
to
Hello,

To help with this, I have put the following method on my basic business
object

class procedure CreateAsInterface(const IID: TGUID; out Obj);

So in my code I do something like

TSomeClass.CreateAsInterface(IWhatever,myvar)

That way, the class never "exists" as an object. The downside is that
if you have to set some variables prior to "casting" as an interface,
you cannot do so. In that case, we use Interface Factories.

David Novo

unread,
Jul 4, 2008, 11:39:51 PM7/4/08
to
This is even implemented for you in the TAggregatedObject class that
comes with Delphi
0 new messages