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

TInterfacedObject

122 views
Skip to first unread message

J. M. De Moor

unread,
Aug 7, 2006, 5:31:28 PM8/7/06
to
(I apologize if I am in the wrong NG...)

I am reading a book on design patterns whose examples are mostly in Java.
So I am trying to work the examples in Delphi. On several occassions
Interfaces are used and the classes that implement them are instantiated and
assigned to fields in other objects. In one example one such field is
changed at run time, where another object that implements the same interface
is assigned to the field.

When I wrote the example in Delphi it worked fine. But I was nervous about
the reference counting with this technique. My (limited) understanding of
the TInterfacedObject is that _Release decrements the count when the object
goes out of scope. So, if I have an interface and 2 classes like this:

type
IMyInterface = interface
...
end;
TMyIClass1 = class(TInterfacedObject, IMyInterface)
...
end;
TMyIClass2 = class(TInterfacedObject, IMyInterface)
...
end;

// in another class have a field:

TMyOtherClass = class(TObject)
private
FMyField: IMyInterface;
...
end;

// then initially assign this way

FMyField := TMyIClass1.Create;

// and subsequently do this

FMyField := TMyIClass2.Create;

Does the reference counting get messed up? Will this introduce a memory
leak of some sort? Or does the assignment of a new InterfacedObject
automatically take the first one out of scope and call the _Release method?

(I hope this made sense.)

Joe


Joanna Carter [TeamB]

unread,
Aug 7, 2006, 5:40:33 PM8/7/06
to
"J. M. De Moor" <nos...@newsreader.com> a écrit dans le message de news:
44d7b12d$1...@newsgroups.borland.com...

| Does the reference counting get messed up? Will this introduce a memory
| leak of some sort? Or does the assignment of a new InterfacedObject
| automatically take the first one out of scope and call the _Release
method?

No, the only time you get problems with reference counting is when you have
both object and interface references to the same object; something that Java
doesn't have problems with.

Joanna

--
Joanna Carter [TeamB]
Consultant Software Engineer


Message has been deleted

Rob Kennedy

unread,
Aug 7, 2006, 5:45:12 PM8/7/06
to
J. M. De Moor wrote:
> (I apologize if I am in the wrong NG...)

This is the right group. You're asking about how a particular facet of
the Delphi language (i.e., interfaces) works.

> I am reading a book on design patterns whose examples are mostly in Java.
> So I am trying to work the examples in Delphi. On several occassions
> Interfaces are used and the classes that implement them are instantiated and
> assigned to fields in other objects. In one example one such field is
> changed at run time, where another object that implements the same interface
> is assigned to the field.
>
> When I wrote the example in Delphi it worked fine. But I was nervous about
> the reference counting with this technique. My (limited) understanding of
> the TInterfacedObject is that _Release decrements the count when the object
> goes out of scope.

Sort of. To begin with, you have the cause and effect reversed. The
_Release method does not cause anything to go out of scope. When an
interface variable goes out of scope, the compiler generates code at
that point to call the object's _Release method.

But scope is just one way for an interface to get released. It can also
be released by having an interface-reference variable get overwritten
with a new value. When a new value gets assigned to a variable, there is
one less reference to the previous value, so the compiler inserts a call
to _Release. Reference-counting _counts_ the number of _references_.

If any call to _Release brings the new reference count to zero, then the
object frees itself. (Look in the implementation of
TInterfacedObject._Release to see one way of doing that.)

> So, if I have an interface and 2 classes like this:
>
> type
> IMyInterface = interface
> ...
> end;

Give a GUID to each of your interfaces. With the cursor after the
"interface" keyword above, press Ctrl+Shift+G.

> TMyIClass1 = class(TInterfacedObject, IMyInterface)
> ...
> end;
> TMyIClass2 = class(TInterfacedObject, IMyInterface)
> ...
> end;
>
> // in another class have a field:
>
> TMyOtherClass = class(TObject)
> private
> FMyField: IMyInterface;
> ...
> end;
>
> // then initially assign this way
>
> FMyField := TMyIClass1.Create;
>
> // and subsequently do this
>
> FMyField := TMyIClass2.Create;
>
> Does the reference counting get messed up?

It shouldn't. Is there anything that suggests your program isn't working
correctly?

> Will this introduce a memory
> leak of some sort? Or does the assignment of a new InterfacedObject
> automatically take the first one out of scope and call the _Release method?

That's what reference counting does. It counts the number of references
to an instance. When the count reaches zero, the object gets destroyed.
Entering and leaving scope is just one way of changing the number of
references. Assigning a new value to a variable is another way, which
has nothing to do with the scope.

--
Rob

Dimitry Timokhov

unread,
Aug 7, 2006, 6:28:18 PM8/7/06
to
Hi, Joe.

A little spoon of tar to the barrel of honey (this is a Russian saying :))

When you are working with interfaces you must be ready to this surprises:
1. http://qc.borland.com/wc/qcmain.aspx?d=31164
2. http://qc.borland.com/wc/qcmain.aspx?d=29238
3. Don't mix objects and interfaces if you are not familiar with interfaces.
If you do this you can get very-very unexpected behavior. The matter is that
Delphi generates code that calls _Release for all local variables of type
equals to _interface_. Therefore if you mix objects and interfaces you can
get direct access to object. Soo you can call free, for example. So if you
call Free before method ends Delphi will try to call _Release of destroyed
method (because delphi has interface variable which was not cleared
automatically when object was destroyed). It's not good :)
For example

procedure ...
var
I: IMyInterface;
O: TMyObject; // implements IMyInterface
begin
O := TMyObject.Create();
I := O;
O.Free();
end;

If you don't get the AV or Privelaged instruction exeptions you are licky :)

This behavior can be fixed like this

procedure ...
var
I: IMyInterface;
O: TMyObject; // implements IMyInterface
begin
O := TMyObject.Create();
I := O;
O.Free();
Pointer(I) := nil; // clear I, soo _Release will not call for I variable.
end;

--
Regards,
Dimitry Timokhov

PS Delphi2006, Prof, Win32, Update2


Message has been deleted
Message has been deleted

Dimitry Timokhov

unread,
Aug 7, 2006, 7:15:49 PM8/7/06
to
Hi, Rudy.

>> When you are working with interfaces you must be ready to this
>> surprises: 1. http://qc.borland.com/wc/qcmain.aspx?d=31164
>

> I already reported this earlier. TSomeClass.Create does not do an
> _AddRef, so the reference count is still 0. Only if you do
> TSomeClass.Create as ISomeInterface the reference count will be 1.
> For the case where the interface is not used as const, an _AddRef will be
> done, but also a _Release.

I know it very well. I only wanted to accent attantion of author of topic on
it.

>> 2. http://qc.borland.com/wc/qcmain.aspx?d=29238
>
> That is not a bug. It is how the compiler works. It creates two unnamed
> interfaces, and each call to CoDemo.CreateDemo assigns one. Only after
> Test finishes, all these items will be released.

I also don't think that it is a bug. Therefore report has type equals to
suggestion.
But I think that such behavior is not soo obvious.

> IOW, this is as designed.

I understant. But is designe good?

> I see it is time for that article on interfaces (especially on interface
> lifetime in Win32) I have planned to write.

It will be very helpfull for the begginers.

J. M. De Moor

unread,
Aug 7, 2006, 8:55:24 PM8/7/06
to
Hey Rob

Thanks for your reply.

> Sort of. To begin with, you have the cause and effect reversed. The
> _Release method does not cause anything to go out of scope. When an
> interface variable goes out of scope, the compiler generates code at that
> point to call the object's _Release method.

OK. I probably didn't articulate it correctly. But I my understanding was
as you state above.


> But scope is just one way for an interface to get released. It can also be
> released by having an interface-reference variable get overwritten with a
> new value. When a new value gets assigned to a variable, there is one less
> reference to the previous value, so the compiler inserts a call to
> _Release. Reference-counting _counts_ the number of _references_.

This is exactly what I wanted to know. Thanks.

> Give a GUID to each of your interfaces. With the cursor after the
> "interface" keyword above, press Ctrl+Shift+G.

Yup. I did do that.

> It shouldn't. Is there anything that suggests your program isn't working
> correctly?

Nope. As I mentioned in my original post, it does seem to work fine. Call
it a comfort post from one not too experienced with interfaces. Hehe.

Joe


Dimitry Timokhov

unread,
Aug 8, 2006, 3:14:29 AM8/8/06
to
Hi, Rudy.

1) ==============

>> > > 2. http://qc.borland.com/wc/qcmain.aspx?d=29238
<skipped>
> I don't see how it could be done otherwise.
> I don't see an alternative. The lifetime resolution of interfaces is
> method level, not statement level, if you know what I mean.

In the report there is a workaround which was made by Sebastian Modersohn.
It's easy way to make program work as I want.
I think that Delphi during compilation can analize - is result of function
(in my example CoDemo.CreateDemo) saved in any local variable?
If result is saved in local variable, then reference counting should work
like it do in now - call _Release on the end of method or if variable is
changed to new value.
If result isn't saved anywhere _Release must be called immediately.

Soo code

begin
Temp:=CoDemo.CreateDemo().DoSomething;
Temp:=CoDemo.CreateDemo().DoSomething;
end;

in fact equals to this code

var
Temp: IDemo; // automatically added by Delphi
begin
Temp:=CoDemo.CreateDemo().DoSomething;
Temp:=nil; // automatically added by Delphi
Temp:=CoDemo.CreateDemo().DoSomething;
Temp:=nil; // automatically added by Delphi
end;

It seems to me that this is the way.

>> > I see it is time for that article on interfaces (especially on
>> > interface lifetime in Win32) I have planned to write.
>>
>> It will be very helpfull for the begginers.
>

> Any suggestions on what should be in it, except the points raised here?

1) Difference between interfaces with GUID and without GUID.
2) Using of *supports* procedures.
3) Using of *as* keyword.
4) Creating interfaced objects without reference counting.
5) Dangerous while mixing objects and interfaces. Some approachs to avoid
them.

Dimitry Timokhov

unread,
Aug 8, 2006, 3:34:00 AM8/8/06
to
Also I think that the mainstream of article must be: interfaces are the
language construction, not bridge to COM.
Also it may be very interesting to advanced programmers to know technical
details of interface implementation.
For example, it was very interesting for me to learn:
1. how interfaces correspond to object VMT.
2. how it implemented that implementation of interface method may be
*virtual*.
3. and so on :)


Message has been deleted

Mark Andrews (The Other One)

unread,
Aug 8, 2006, 6:16:06 AM8/8/06
to
Hi Rudy,

> Any suggestions on what should be in it, except the points raised here?

I think an article is an excellent idea. Raw interfaces (that is,
without COM) are one of the greatest features of Delphi and yet they
aren't as widely known as they should be. We find them an indispensable
tool in architecting extensible and flexible products, so anything done
to raise the profile and awareness of interfaces has got to be good.

Delayed calls to _Release in compiler-generated temporary variables
caught me out a few years ago, so I think that's worth covering. My case
involved a TInterfacedObject and a TContainedObject and I wasn't
expecting something like this

OuterIntf.InnerIntf.Method()

to generate temporaries. (Details here: http://tinyurl.com/f8xgm)

Cheers,
Mark.

Dimitry Timokhov

unread,
Aug 8, 2006, 7:19:05 AM8/8/06
to
Hi, Rudy.

> And that will work, but that is not what the compiler can do for you. The
> compiler does not know what you want.

I cannot agree with you at all.
I dont think that compiler should know what I want (I know it myself).
But compiler should work logical. Now I'll try to explain where I see not
logical behavior.

Let's see this code (this is code from my report, but Test procedure
changed).

program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
IDemo=interface
['{48FE2E29-4647-4437-A38C-F25B0F94663B}']
Procedure DoSomething;
end;
TDemo=Class(TInterfacedObject,IDemo)
public
Procedure DoSomething;
Procedure BeforeDestruction; override;
end;
CoDemo=Class
Class Function CreateDemo:IDemo;
end;
Procedure TDemo.DoSomething;
begin
Writeln('Method DoSomething was called');
end;
Procedure TDemo.BeforeDestruction;
begin
Writeln('Beeing destroyed');
end;
class function CoDemo.CreateDemo: IDemo;
begin
Result:=TDemo.Create();
end;
procedure Test();
var
kI: Integer;
begin
for kI := 0 to 1 do
CoDemo.CreateDemo().DoSomething;
WriteLn('Finish on method Test');
end;
begin
Test();
WriteLn('Finish of program');
ReadLn;
end.

Can you say me without executing the program about console output?
I guess that *you* will do it OK. But can you explain to begginers what is
the difference between

procedure Test();
var
kI: Integer;
begin
for kI := 0 to 1 do
CoDemo.CreateDemo().DoSomething;
WriteLn('Finish on method Test');
end;

and

procedure Test();
begin
CoDemo.CreateDemo().DoSomething;
CoDemo.CreateDemo().DoSomething;
WriteLn('Finish on method Test');
end;

?

--
Regards,
Dimitry Timokhov


Message has been deleted

Dimitry Timokhov

unread,
Aug 8, 2006, 8:00:37 AM8/8/06
to
Hi, Rudy.

It seems to me that I finally agree with you :)
You gave very good explanation. Of couse I know how it works. But I cannot
find logical explanation for that.
I think it is good to place this nicety to your future article.

I'll close my report and give reference to this topic.

David M

unread,
Aug 9, 2006, 1:16:32 AM8/9/06
to
> Any suggestions on what should be in it, except the points raised here?
>

Perhaps the C++ side of interfaces? Mixing C++ and VCL can be interesting,
sometimes. I'd especially be interested in C++ descendants of
TInterfacedObject, TAutoIntfObject, etc (I suppose doing that is possible,
but would there be problems? Is it an easier way to COM support than the
ATL route?)

Message has been deleted

Dimitry Timokhov

unread,
Aug 9, 2006, 3:48:42 PM8/9/06
to
Hi, Rudy.

I want to advaice you, don't write article with many words and few source
code - good source code, *based on you experience*, with a few necessary
comments - this is the best article!

Where are in internet may articles with common words - "wow, interfaces -
it's good, it's very good, wow, wow" - but a few source code :(
Public benefit of such article is very small. More practice!

PS. IMHO

0 new messages