I recently came across (again) the Delphi compiler code-gen bug when passing an interface as const
leaks a reference.
This happens if your method is declared to pass an interface variable as const
, e.g.:
procedure Frob(const Grob: IGrobber);
and the fix is to simply remove the const
:
procedure Frob(Grob: IGrobber);
I understand that const
(and var
, and out
) allow you to pass items by reference. In the case of a structure this saves an argument copy; letting you instead simply pass the pointer to the item.
In the case of an Object
/Pointer
/Interface
there is no need to pass by reference, since it is a reference; it already will fit in a register.
In order to never have this problem again, i went on a crusade. I searched all my source trees for:
const [A-Za-z]+\: I[A-Z]
And i removed about 150 instances where i pass an interface as const.
But there are ones that i cannot change. The TWebBrowser
callback events are declared as:
OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
\___/
|
?
Have i gone too far? Have i done a bad thing?
Edit: Or, to phrase it in a less "based on opinion" style question: are there any serious down-sides to not passing an interface as const?
Bonus: When Delphi does not (always) increment a interface reference count they are violating The Rules of COM:
Reference-Counting Rules
Rule 1: AddRef must be called for every new copy of an interface pointer, and Release called for every destruction of an interface pointer, except where subsequent rules explicitly permit otherwise.
Rule 2: Special knowledge on the part of a piece of code of the relationships of the beginnings and the endings of the lifetimes of two or more copies of an interface pointer can allow AddRef/Release pairs to be omitted.
So while it may be an optimization that the compiler can take advantage of, it has to do it correctly so as to not violate the rules.
A Java interface can contain constants. In some cases it can make sense to define constants in an interface. Especially if those constants are to be used by the classes implementing the interface, e.g. in calculations, or as parameters to some of the methods in the interface.
Yes, you can pass Interface as a parameter in the function.
It's possible for interfaces to have constants. Interface constants work exactly like class constants. Prior to PHP 8.1. 0, they cannot be overridden by a class/interface that inherits them.
Apparently C# cannot define a constant associated with an interface.
This happens if your method is declared to pass an interface variable as const, e.g.:
procedure Frob(const Grob: IGrobber);
That's not quite right. In order for there to be a leak, you need for there to be nothing in the code that ever takes a reference to the newly created object. So if you write:
Frob(grob);
There there's no problem because the interface grob
already has at least one reference.
The problem arises when you write:
Frob(TGrobberImplementer.Create);
In that scenario nothing takes a reference to the interface and so it is leaked. Well, it will be leaked so long as nothing in the implementation of Frob
takes a reference to it.
Have I done a bad thing?
Well, that depends. I don't think anything particularly bad will come of what you have done. There is a downside in terms of performance because all of your functions that accept interface parameters will now have to add and release references, using implicit try/finally blocks. Only you can judge whether that matters.
The more significant issue relates to code that is outside of your control. You give
procedure OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
as an example. There's no issue there because you never call that method. It's an event handler that you implement. The framework calls it, and it passes an interface that is already referenced.
The real problem comes from methods declared in the RTL or any other third party code that you call. If you are calling the methods, and if they use const
interface arguments then you can fall into the trap.
It's easy enough to work around, albeit tiresome.
grob := TGrobberImplementer.Create;
Frob(grob);
My reasoning to deal with the issue goes like this:
const
interface parameters at least some of the time.const
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With