3

I have this simple program, written in Delphi 10.2, and running nicely on Windows, but crashing on Linux.

The essence is that the class used has code to be executed in its destructor.

type
  Kwak = class
  public
    index: integer;
    constructor Create(index:integer);
    destructor Free;
  end;

constructor Kwak.Create(index:integer);
begin
  self.index := index;
  writeln('Welcome Kwak '+inttostr(index));
end;

destructor Kwak.Free;
begin
  writeln('Bye Kwak '+inttostr(index));
end;

If I use this in a calling procedure, like this one:

procedure myProc1;
var 
  myKwak:Kwak;
begin
  myKwak := Kwak.Create(15);
  myKwak.Free;
end;

This runs fine on Windows, but causes a segmentation error on Linux the moment myKwak leaves scope (the end is encountered in myProc1).

I guess this all has to do with Automatic Reference Counting on Linux compiler.

If I use FreeAndNil(), the program doesn't crash, but doesn't call the destructor either.

What is an elegant solution?

  • There are many Free's like that in my program. Of course, transferring the Free code to something else is possible, but I would like it more elegant.
  • Program needs to compile in Windows back to XE2, on Linux on 10.2. I read 10.3 leaves out ARC, which may solve the issue, but 10.3 is costly.
  • Program changes needed and {$IFDEF ...} directives preferably minimized.

Please tell me your suggestions.

LU RD
  • 34,438
  • 5
  • 88
  • 296
  • 2
    Define (and override) `Destroy()` as destructor, not `Free()`. Calling `Free()` to destroy your instance is correct nonetheless. See [this example](https://stackoverflow.com/a/30515358/4299358) – AmigoJack Jul 18 '21 at 17:48

1 Answers1

7
destructor Free; // <-- WRONG!

This is wrong. The correct destructor is called Destroy() instead, and it is virtual in TObject so you need to override it in derived classes:

type 
  Kwak = class
  public
    index: integer;
    constructor Create(index:integer);
    destructor Destroy; override;
  end;

constructor Kwak.Create(index:integer);
begin
  inherited Create;
  self.index := index;
  writeln('Welcome Kwak '+inttostr(index));
end;

destructor Kwak.Destroy;
begin
  writeln('Bye Kwak '+inttostr(index));
  inherited;
end;

On non-ARC systems, TObject.Free() is a non-virtual instance method that calls the Destroy() destructor if Self is not nil.

On ARC systems, the compiler silently replaces all calls to Free() with a nil assignment, decrementing an object's reference count. This is so the same code can be used with similar semantics on both ARC and non-ARC systems. An object's Destroy() destructor is called when its reference count falls to 0.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Worth noting that, as of 10.3 Rio, the linux compiler has [ditched ARC](https://blogs.embarcadero.com/directions-for-arc-memory-management-in-delphi/) and is going back to traditional memory management (to mirror the Windows compiler). At this point, it's almost worth it to just upgrade and forget about ARC because it's going away anyway. Not that it impacts this code, but if the rest of OP's code is leaning on ARC for memory management then upgrading beyond Tokyo will require a significant refactor. – J... Jul 19 '21 at 17:06
  • 1
    @J... ARC has already gone away, as [10.4 ditched ARC for ALL platforms](http://docwiki.embarcadero.com/RADStudio/Sydney/en/What%27s_New#Unified_Memory_Management). – Remy Lebeau Jul 19 '21 at 17:21
  • Indeed, I noted Tokyo/Rio because OP is working with Linux, but yes, ARC is an entirely dead end. Perhaps "going away" was poor phrasing - I meant from OP's perspective as regards their upgrade prospects. Investing in new development of ARC-dependent code now is just digging a technical debt hole. – J... Jul 19 '21 at 17:30
  • Thanks Remy Replacing the destructor free with destroy (made overwrite) makes it work like a charm. Thank you very much for helping. – JP Hendriks Jul 21 '21 at 18:16