8

I have a helper class that will be in wide use across the application. The implementation relies on interface reference counting, the idea roughly is:

...
var
  lHelper: IMyHelper;
begin
  lHelper := TMyHelper.Create(some params);
  ...some code that doesn't have to access lHelper
end;

So the implementation relies on IMyHelper going out of scope at the end of the method, but not before.

So what am asking is, can I be certain that in some future Delphi compiler won't play smart and release the interface right after it's created if the variable is not accessed in rest of the method ?

  • 1
    The comments to [this blog post](http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html) might be interesting to you. – Uli Gerhardt Feb 01 '12 at 09:34
  • Nobody can be certain about the future, but the compiler behavior under the question should be considered highly improbable and undesirable. – kludg Feb 01 '12 at 09:41
  • Whats the point of worrying about unused instance? – OnTheFly Feb 01 '12 at 09:45
  • I asked a similar question here: http://stackoverflow.com/questions/7759081/implicit-interface-variables – David Heffernan Feb 01 '12 at 09:50
  • user539484 measuring procedure execution time maybe? – mjn Feb 01 '12 at 09:50
  • 1
    @mjn That is what use do for [our profiling feature in our logging class](http://blog.synopse.info/post/2011/04/14/Enhanced-logging-in-SynCommons): writing `TSynLog.Enter;` at the beginning of the method will automatically profile it, and generate a "leave" log entry when the interface returned by function TSynLog.Enter. – Arnaud Bouchez Feb 01 '12 at 10:00
  • Make sure, and also take care of an end logevent. This is how problems start. – Marco van de Voort Feb 01 '12 at 10:43
  • +1 for the question. I'm also using this method for local garbage collector, garbage collector on chained methods and profiler. – user315561 Feb 23 '12 at 19:16

2 Answers2

10

IMHO you can be confident of that. The out-of-scope pattern will probably remain global to the instruction block of this method. This would be a breaking change.

See this comment from Barry Kelly (from Embarcadero):

As to your earlier comment, about explicit variables: in the hypothetical (and breaking change) case, where we optimized interface variable usage, we would likely not only break the described RAII-like functionality but also the explicit variable approach too; the values assigned to FooNotifier and BarNotifier are not used, so "in theory" they can be freed up sooner, and potentially even reuse the same storage.

But of course, destruction of the interface can have side-effects, and that's what's being relied upon for the effect in the post. Changing the language such that side-effects like these have visible changes is not something we do willingly.

So you can guess that Embarcadero won't introduce any backward compatibility change here. The benefit of re-using an interface memory won't be worth breaking compatibility and introducing side effects: saving a pointer (4 or 8 bytes) is not worth it nowadays, especially when the stack is already allocated, and aligned (x64 model uses more stack than x86).

Only if a Garbage Collector is introduced to the language (which I do not want from my personal point of view), objects life time may change. But in this case, life time may probably be longer.

In all cases, you can create your own code, to be sure that it will released at the end of the method:

var
  lHelper: IMyHelper;
begin
  lHelper := TMyHelper.Create(some params);
  try 
    ...some code that doesn't have to access lHelper
  finally
    lHelper := nil; // release the interface count by yourself
  end;
end;

In fact, this is the code already generated by the compiler. Writing this will be perfectly redundant, but it will ensure that compiler won't cheat on you.

When speaking of interfaces and reference counting, please take in account the potential issue of circular references in Delphi. See this great article (i.e. "Example 2-15") about the need of "weak pointers" for circular references of Interfaces.

Other languages (like Java or C#) use a garbage collector to resolve this. Objective C uses an explicit "zeroing weak pointers" mechanism to solve it - see this discussion or this SO answer for a potential implementation. Perhaps future version of Delphi may consider using an implementation similar to the ARC model introduced in Objective C. But I suspect there will be an explicit syntax to preserve compatibility with existing code.

Community
  • 1
  • 1
Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159
  • The idea is to avoid having to write try..finally, and in fact in this case I don't think it's needed, lHelper := nil at the end is sufficient, if there is an exception then it will exit scope and release the interface. Having said that, I'd rather not write lHepler := nil either. –  Feb 01 '12 at 10:39
  • @DanielMaurić You don't need to. The current language guide states that it will be destroyed at the end of scope. Nobody can guarantee the future for anything. Heck, they could change begin/end to start/finish, but it's not likely is it. – David Heffernan Feb 01 '12 at 10:46
  • What we can say for sure, is that such a change, if it was to be DONE, would block many people from every upgrading their codebases, and would kill the product sales, ergo it won't happen. – Warren P Feb 01 '12 at 14:54
  • @Warren: Some people would put the Unicode switch in the same category. – Uli Gerhardt Feb 01 '12 at 15:57
  • That's an example of a switch that people would THINK was a good idea, until they experienced the reality, which is that it would create a mess from which there was no recovery, and it would have killed Delphi completely. Engineers should NOT listen to their customers when customers are being idiots, and this proves that the Engineering team in the RAD Studio product are not overruled by a bunch of idiots with MBAs and suits who believe that "the customer is always right". – Warren P Feb 01 '12 at 16:36
  • I've added a link to http://stackoverflow.com/questions/487311/how-to-implement-reference-counted-objects-in-delphi#487387 which is IMHO the most complete approach of weak references with Delphi interfaces. In all cases, not an easy task! – Arnaud Bouchez Feb 01 '12 at 17:26
4

The documentation says this (emphasis mine):

On the Win32 platform, interface references are typically managed through reference-counting, which depends on the _AddRef and _Release methods inherited from System/IInterface. Using the default implementation of reference counting, when an object is referenced only through interfaces, there is no need to destroy it manually; the object is automatically destroyed when the last reference to it goes out of scope.

The scope of a local variable is the method and so the current specification is that _Release will not be called until the method is complete.

There's never a promise that specifications will not be changed in the future but I think the likelihood of a change being made to this part of the language is vanishingly small.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490