6

I have method with a parameter as object (sniped code below):

TMyObject=class(TObject)
  constructor Create();
  destructor Destroy();override;
end;

implementation

function doSomething(x:TMyObject):integer;
begin
  //code
end;

procedure test();
var
  w:integer;
begin
  w:=doSomething(TMyObject.Create);
  //here: how to free the created object in line above?
end;

How destroy object created inside of called method doSomething outside of this method?

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
Artik
  • 803
  • 14
  • 36
  • 10
    Don't be lazy. Create a variable! – smooty86 Aug 25 '16 at 09:27
  • yes, i did it but i think is it possible any different way? – Artik Aug 25 '16 at 09:29
  • use interfaces instead of objects :) – kami Aug 25 '16 at 09:32
  • @kami interfaces cannot be used because of compiler bug and will cause memory leaks. – Dalija Prasnikar Aug 25 '16 at 09:35
  • @DalijaPrasnikar `because of compiler bug` ??? What do you mean? Interfaces in Delphi are broken? – kami Aug 25 '16 at 09:43
  • 2
    @DalijaPrasnikar http://stackoverflow.com/a/7640979/505088 – David Heffernan Aug 25 '16 at 09:45
  • @DavidHeffernan Thanks. Issue is reported as http://qc.embarcadero.com/wc/qcmain.aspx?d=90482, I am searching for SO post where Barry Kelly said it is compiler bug. My Google fu is failing me :( – Dalija Prasnikar Aug 25 '16 at 09:47
  • 4
    @DalijaPrasnikar http://stackoverflow.com/a/4510268/ The force is strong with me today! – David Heffernan Aug 25 '16 at 09:53
  • 3
    @DalijaPrasnikar, there is a leak only when the interfaced parameter is `const`. Am I right? – kobik Aug 25 '16 at 10:06
  • 1
    @kobik yes, but even if you "can make it work" if you don't use `const` such code would be extremely fragile. I would never recommend using it. So creating additional variable is the only right way to go. – Dalija Prasnikar Aug 25 '16 at 10:08
  • 2
    @kobik: you can use `const` as well, but it will leak if you pass it as `doSomething(TMyObject.Create);`. It will **not** leak if you pass it as `doSomething(TMyObject.Create as IMyInterface);`. I always use the latter form. – Rudy Velthuis Aug 25 '16 at 11:09
  • 1
    @DalijaPrasnikar: When the parameter is **not** `const`, there is absolutely nothing fragile about it. And if the parameter is `const`, passing `TMyObject.Create as IMyInterface` is rock solid as well. No fragility whatsoever, unless you try to defeat refcounting by something like `Pointer(AInterfaceParameter) := nil;`. – Rudy Velthuis Aug 25 '16 at 11:15
  • @DalijaPrasnikar: See the second comment on the QC, about using `as`. But no, interfaces are not broken. – Rudy Velthuis Aug 25 '16 at 11:21
  • @RudyVelthuis You are free to use it if you like. But it is not something I would use, so I cannot advise it. Simplest thing to remember is that you have to have additional variable, regardless of using it as object or interface reference. That is simple rule, easy to remember. When you start dancing around such issues with uncommon coding patterns, it is very likely that someone will came along and do something stupid. – Dalija Prasnikar Aug 25 '16 at 11:39
  • 1
    @RudyVelthuis, In order to use `TMyObject.Create as IMyInterface` you must define a GUID for the interface. (true for D5) – kobik Aug 25 '16 at 11:46
  • 1
    Sure, assigning to a variable is always safe. But with the `as` cast, it is not fragile. As I said, interfaces are in no way broken. Such a cast is not "dancing around with uncommon code patterns", it is a proper way to do this. – Rudy Velthuis Aug 25 '16 at 11:46
  • @kobik: that is assumed. I would never declare interface types without GUID, and if you forget, it will not compile, so that is not a big problem. – Rudy Velthuis Aug 25 '16 at 11:50
  • @RudyVelthuis, you can declare interface types without guid and it compiles. See http://stackoverflow.com/questions/2992183/are-guids-necessary-to-use-interfaces-in-delphi – LU RD Aug 25 '16 at 13:41
  • @LURD: Yes, you can compile them without GUID, but then you can not use `as` with them, so if you use `as` and forgot the GUID, the compiler will tell you you can't do that. In other words, you get a compile time error, which is no big deal. – Rudy Velthuis Aug 25 '16 at 13:44

1 Answers1

10

In order to free the object instance, you need to have a reference to it on which you can call Free().

Since you are creating the object instance in-place as a parameter, the only reference you will have is the one inside of the doSomething() parameter.

You either have to Free it inside of doSomething() (which is practice I would not advise doing):

function doSomething(x: TMyObject): Integer;
begin
  try
    //code
  finally
    x.Free;
  end;
end;

Or, you need to create an additional variable in test(), pass it to doSomething(), and then Free it after doSomething() returns:

procedure test();
var
  w: Integer;
  o: TMyObject
begin
  o := TMyObject.Create;
  try
    w := doSomething(o);
  finally
    o.Free;
  end;
end;

While one may think that using a reference counted object would allow you to create the object in-place and let reference counting free the object, this kind of construction may not work because of the following compiler issue:

The compiler should keep a hidden reference when passing freshly created object instances directly as const interface parameters

This is confirmed by a former Embarcadero compiler engineer, Barry Kelly, in a StackOverflow answer:

Should the compiler hint/warn when passing object instances directly as const interface parameters?

Community
  • 1
  • 1
Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • 1
    It will work if the interface parameter is not `const` or if it is passed with `as ISomeInterface`. See my other comments. Your comment about interfaces being broken is a bit panicky and not really true. – Rudy Velthuis Aug 25 '16 at 11:26
  • @RudyVelthuis Yes, it will work like that. But it is extremely fragile code because if you change method signature you can break code in completely unrelated place - method call. I would not advise it. And interfaces are broken, because numerous Delphi developers constantly write code that is broken with above bug. Even experienced ones. Every now and then I have to tell people about this bug. – Dalija Prasnikar Aug 25 '16 at 11:34
  • 3
    Changing a signature is always a bad idea. But using `as`, it is never fragile, either with or without `const`. – Rudy Velthuis Aug 25 '16 at 11:38