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?
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]);