1
votes

I have a FMX TCombobox in my Delphi 10.3.3 app that I'm compiling for Android. I had previously researched the subject and found Using Primitive Types with TStrings in iOS. I downloaded BoxPrimitives.pas and the code below, compiled sucessfully under Delphi 10.3.3.

uses BoxPrimitives;

{$ifdef ANDROID}
cbGender.Items.AddObject('Male', TBoxInteger(1));
cbGender.Items.AddObject('Female', TBoxInteger(0));
{$else}
cbGender.Items.AddObject('Male', TObject(1));
cbGender.Items.AddObject('Female', TObject(0));
{$endif}

Now I've opened that source code in Delphi 10.4.1, and the BoxPrimitives.Pas no longer compiles.

[DCC Error] BoxPrimitives.pas(46): E2123 PROCEDURE, FUNCTION, PROPERTY, or VAR expected

The general question then is: What changed in Delphi 10.4 that's preventing it from compiling? And more specifically: Is there a way to use AddObject with a TCombobox in Android in Delphi 10.4?

2
10.4 no longer has ARC compiler, Windows code should work again on Android.Dalija Prasnikar

2 Answers

3
votes

In RAD Studio 10.4, Embarcadero decided to remove ARC for object lifetime management on its mobile platforms.

What's New in RAD Studio 10.4 Sydney

Unified Memory Management

  • Delphi memory management is now unified across all supported platforms - mobile, desktop, and server - using the classic implementation of object memory management. Compared to Automatic Reference Counting (ARC), this offers better compatibility with existing code and simpler coding for components, libraries, and end-user applications. The ARC model remains for string management and interface type references for all platforms.

  • For C++, this change means that the creation and deletion of Delphi-style classes in C++ follow normal memory management just like any heap-allocated C++ class, significantly reducing complexity.

The correct way to detect when ARC object lifetime management is being used, across all Delphi compiler versions and platforms, is to use {$IFDEF AUTOREFCOUNT}. This is even mentioned in the BoxPrimitives.pas unit:

this class is only for use by AUTOREFCOUNT compilers

For example:

{$ifdef AUTOREFCOUNT}
uses BoxPrimitives;
{$endif}

{$ifdef AUTOREFCOUNT}
cbGender.Items.AddObject('Male', TBoxInteger(1));
cbGender.Items.AddObject('Female', TBoxInteger(0));
{$else}
cbGender.Items.AddObject('Male', TObject(1));
cbGender.Items.AddObject('Female', TObject(0));
{$endif}

...

var Value: Integer;
{$ifdef AUTOREFCOUNT}
Value := TBoxInteger(cbGender.Items.Objects[index]);
{$else}
Value := Integer(cbGender.Items.Objects[index]);
{$endif}

That being said, I would probably take this a step further, by editing BoxPrimitives.pas to surround its code with {$IFDEF AUTOREFCOUNT} ... {$ELSE} ... {$ENDIF}, and then in the {ELSE} section define TBoxInteger (and other types) as simple aliases, eg:

interface

{$ifdef AUTOREFCOUNT}

uses
  Classes, Types, Generics.Defaults;

type
  TRSBoxPrimitive<T> = class(TObject)
    ...
  end;

  TBoxInteger = TRSBoxPrimitive<Integer>;
  TUnboxInteger = TBoxInteger;
  ...

{$else}

type
  TBoxInteger = TObject;
  TUnboxInteger = Integer;
  ...

{$endif}

implementation

{$ifdef AUTOREFCOUNT}

...

{$endif}

end.

Then you wouldn't need to {$IFDEF} your AddObject() calls at all, just use TBoxInteger and TUnboxInteger unconditionally on all platforms, eg:

// look ma, no IFDEF's!
uses BoxPrimitives;

cbGender.Items.AddObject('Male', TBoxInteger(1));
cbGender.Items.AddObject('Female', TBoxInteger(0));
...
var Value: Integer;
Value := TUnboxInteger(cbGender.Items.Objects[index]);
1
votes

Based on Dalija's comment, the answer then is to modify the code so it looks like this:

{$if defined(Android) and (CompilerVersion < 34)}
uses BoxPrimitives
{$endif}

{$if defined(Android) and (CompilerVersion < 34)}
cbGender.Items.AddObject('Male', TBoxInteger(1));
cbGender.Items.AddObject('Female', TBoxInteger(0));
{$else}
cbGender.Items.AddObject('Male', TObject(1));
cbGender.Items.AddObject('Female', TObject(0));
{$endif}