Code

Typableitung in generischen Methoden

Durch ein interessantes Code-Beispiel, hinter dem von Kim Madsen zunächst ein möglicher Bug im Delphi Compiler vermutet wurde, habe ich mir das Aufruf-Verhalten von überladenen Methoden, die auch generische Typ Parameter enthalten, genauer angesehen. Ich war von dem möglichen Fehler nämlich überrascht, da ich ziemlich sicher in aktuellen Projekten Overloads mit Kombinationen aus generischen und nicht-generischen Methoden verwende, ohne das offensichtliche Fehler auftreten.

Hier eine vereinfachte Darstellung von Kim’s Code:

TFoo = class(TObject)
  public
    function DoSomething(SomeObject: TValue): string; overload;
    function DoSomething<T>(SomeObject: T): string; overload;
  end;

TBar = class(TObject)
end;

//...

var Foo := TFoo.Create;
var Bar := TBar.Create;

Foo.DoSomething(Bar);

Welche der beiden möglichen Varianten von DoSomething sollte durch Foo.DoSomething(Bar) aufgerufen werden?

Im ersten Ansatz war ich geneigt zu sagen, dass die nicht-generische Variante, also function DoSomething(SomeObject: TValue): string; aufgrufen werden sollte, da ja schliesslich kein Typ-Parameter angegeben ist und daher die erste Variante die am ehesten passende ist.

Tatsächlich wird aber die generische, zweite Variante aufgerufen. Ich hatte zunächst „TValue“ im Verdacht, hier die Overload-Auflösung zu „blockieren“, da TValue ja quasi ein „künstlicher“ RTTI-.getriebener Typ ist.

Stefan Glienke hatte aber letztlich den passenden Hinweis:

Generische Parameter sind tatsächlich optional und der Typ wird durch den Typ der Variablen, die übergeben wird, automatisch abgeleitet. Diese „Type-Inferrence“ hat Vorrang vor dem Auflösen von Overloads und erzeugt damit einen besseren Match als die Methode mit „TValue“.

Die Typableitung bei generischen Methoden ist übrigens hier dokumentiert.

Der folgende Code ruft also tatsächlich eine generische Implementation auf, obwohl kein Typ-Parameter im Aufruf angegeben ist:

function DoSomething<T>(SomeObject: T): string;
//...
Foo.DoSomething(Bar); //<T> is inferred from "Bar"

Hier ein Gist mit komplettem Code zum Ausprobieren:

https://gist.github.com/omonien/24b9b4ba64d46bdfa478615e8e9b0ae6

Embarcadero MVP des Jahres 2019 – DACH Region

Uwe Raabe und ich sind die MVP Koordinatoren der DACH Region und haben u.a. die Aufgabe, einen MVP des Jahres auszuwählen.

Da wir dies nicht irgendwie ausklüngeln wollen, haben wir uns entschlossen, der Delphi Community die demokratische Möglichkeit zu geben, den MVP des Jahres zu wählen.

Auf dieser Seite könnt ihr ab sofort, bis zum kommenden Freitag, den 13. Dezember 2019, eure Stimme für den MVP abgeben, der euch im – schon fast vergangenen – Jahre 2019 am meisten beeindruckt hat, dessen Blogs ihr am informativsten fandet oder von dem ihr einfach denkt, er soll Titel für dieses Jahr erhalten.

Was gewinnt der MVP des Jahres? Ganz viel Ruhm – mit „h“ 🙂

Jim McKeeth, sozusagen der MVP Oberaufseher, wird entsprechende Posts verfassen und den Gewinner damit entsprechend würdigen. Flugreisen, Fernseher oder Millionen von Dollar zu spendieren, davon konnten wir Jim leider nicht überzeugen …

Die Abstimmung erfolgt anonym. Jeder, der sich der Embarcadero Community zugehörig fühlt, hat eine Stimme. MVPs können selbstverständlich mit abstimmen und dürfen sich natürlich auch selbst wählen.

Uwe und ich sind auch wählbar – auch wenn wir uns hier eher als Organisatoren sehen.

Das Ergebnis werden wir nach Ablauf der Abstimmung hier veröffentlichen. Wir werden nur den Sieger benennen, also kein Ranking erstellen.

Update: die Abstimmung ist inzwischen beendet. Das Ergebnis findet sich hier.

4. Deutsche Coderage 26.06.2019 – TMessage vs. MessageDlg

Am 26.06.2019 fand eine weitere Ausgabe der Deutschen CodeRage als Online-Konferenz statt.

Mein Beitrag, der um 17:00 MESZ zu sehen war, behandelt das das Thema TMessage und TMessageDlg: Wie triggert man aus Businesslogik Benutzerabfragen ohne sich an die UI zu koppeln?

TMessage vs MessageDlg

Weiterhin gibt es noch zahlreiche interessante Beiträge meiner Kollegen Matthias Eißing, Dr. Holger Flick, Volker Hillmann, Frank Lauter, Olaf Monien, Uwe Raabe, Bernd Ua und Daniel Wolf.

Die Anmeldung, der Zeitplan und weitere Informationen finden sich auf der CodeRage Seite.

Hier der gezeigte Quelltext, erweitert noch um eine TMessage, die ein einfaches ShowMessage anfordern kann.
Das Video ist auf meinem Youtube-Kanal zu finden.

Signieren von Windows Delphi Anwendungen

FireMonkey Apps für iOS und Android werden in Delphi im Rahmen des Bereitstellungsprozesses automatisch signiert – dies ist inbesondere für iOS Apps unabdingbar und wurde daher in der Delphi IDE auch entsprechend umgesetzt.

Bei Windows Anwendungen (egal ob 32 oder 64 Bit) ist dagegen keine Signatur-Option in Delphi zu finden. Dies dürfte dazu führen, dass viele Delphi Apps unsigniert verteilt werden. Dies ist jedoch spätestens seit Windows 10 nicht mehr zu empfehlen, denn der mit Windows 10 eingeführte „SmartScreen-Filter“ blockiert standardmäßig „nicht vertrauenswürdige“ Anwendungen. Eine unsignierte Anwendung (EXE) ist per se „nicht vertrauenswürdig“, da ihre Herkunft nicht verifiziert werden kann.

Read More

Delphi and Xcode 8.3.x deployment solution

Since Apple updated Xcode to version 8.3.x, iOS IPA deployment is broken with Delphi up to 10.2 (Tokyo). There is an official workaround, which basically instructs to download Xcode 8.2 and use that for now:

http://docwiki.embarcadero.com/PlatformStatus/en/Main_Page#iOS_10

Unfortunately though, Apple started sending out notifications, that it won’t accept any builds created with Xcode versions older than 8.3.2 anymore. This is what I’ve received from Apple a few days ago:

„Dear developer,
We have discovered one or more issues with your recent delivery for „AppEvents“. Your delivery was successful, but you may wish to correct the following issues in your next delivery:
Deprecated Xcode Build – Due to resolved app archives issues, we will be deprecating Xcode 8.3 on May 10, 2017, at which time app archives built with 8.3 will no longer be accepted by the App Store. Download Xcode 8.3.2 or newer, rebuild your app and resubmit.
After you’ve corrected the issues, you can use Xcode or Application Loader to upload a new binary to iTunes Connect.
Regards,
The App Store team“

The problem with Delphi and Xcode 8.3 is basically a tool, that Apple decided to remove from its tool chain, but which Delphi still depends on. So the solution is to copy that missing tool „PackageApp“ from 8.2 to 8.3. Below are the steps that made my Delphi 10.2 (Tokyo) iOS deployment fully working again:

  1. Install current Xcode from Appstore into your applications folder – currently 8.3.2
  2. Download Xcode 8.2.1 from
    https://developer.apple.com/download/more/
  3. Unzip Xcode_8.2.1.xip to a separate folder (e.g. /tmp/Xcode)
  4. Make a backup copy of your current Xcode version:
    cp -R /Applications/Xcode.app /tmp/Xcode.8.3.app (might take a minute or two)
  5. Copy the following file from 8.2.1 to your current Xcode app:
    sudo cp /tmp/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/

Do not forget to add Export/Encryption exemption to your Delphi iOS apps

Apple requires you to specify if your apps are using any form of encryption. If you do not use encryption, you have to confirm that in your app settings on iTunes Connect – this is not new, Apple started asking for that long time ago. Important to notice is though that since a while Apple requires your app to have a special entry in it’s info.plist file – even if do not use any encryption. Without that your app will fail to verify for Testflight and Appstore deliveries.

 

It’s easy to add that entry in Delphi. Go to Project – Options – Version information. The list/grid is actually editable, just go to the last row and press cursor down. Then add „ITSAppUsesNonExemptEncryption – false“ as shown in the screenshot

MVVM Anwendungen mit Delphi und nicht gegen Delphi

Im folgenden Video meines Youtube-Kanals eine Einführung zum Thema MVVM Anwendungen mit Delphi. Wichtige Motivation ist für mich, dass ich soviel Delphi wie möglich verwende, also eben nicht anfange, alles von Hand im Source-Editor hinzuschreiben.

Hinweis: Das Video versteht sich als erste Einführung in das Thema MVVM mit Delphi. Selbstverständlich gibt es noch eine ganze Reihe von Dingen, die man optimieren und ausbauen kann. Weitere Schritte werde ich hier in noch folgenden Beiträgen skizzieren.

Das Video ist in Deutsch.

TTask / TThreadPool may keep interfaced objects alive

UPDATE: The issue described here has been marked „resolved“ on May, 5th 2017, so we should see corrected behaviour in one of the next deliveries.

I am using TTask.Run quite a lot in projects and in many of my demos. I won’t go into the details of how to use TTask here, but I only want to highlight that there is still an open issue, which may cause interfaced objects to be destroyed much later, than you would expect. Ultimately this may lead to a much higher memory footprint of your application.

As of Delphi 10.1 Update 2 the issue is still marked open on quality.emabaracdero.com – you might vote for it, if your code is affected by it.

Interfaced objects use automatic reference counting for lifecycle management:

var
  LFoo : IFoo
begin
  LFoo := TFoo.Create;
  //work with LFoo
end;  //LFoo goes out of scope, its reference counter will go to zero,
      // thus it will be destroyed here automatically.

With TTask.Run you can start background threads, that will execute code passed in as anonymous method:

var
  LFoo : IFoo
begin
  LFoo := TFoo.Create;
  TTask.Run(procedure
    begin
      Foobar(LFoo); //Work with LFoo 
    end;
  ).Waitfor; //Wait for task/thread to be completed 
end;  //LFoo goes out of scope, its reference counter SHOULD go to zero,
      // thus it SHOULD be destroyed here automatically.   

Unfortunately the above does not work as expected. The internals of TTask (actually TThreadpool) keep a reference to the (already finished) task and prevent the instance of TFoo, which is referenced by LFoo, to be destroyed automatically. At least when the TThreadPool gets destroyed, all those kept references will be released and the instances will be destroyed, thus they won’t report as memory leak (with ReportMemoryLeaksOnShutDown).

In other words: you will have to set LFoo to nil manually – until this issue gets solved in one of the next updates.

  TTask.Run(procedure
    begin
      Foobar(LFoo); //Work with LFoo 
    end;
  ).Waitfor; //Wait for task/thread to be completed 
  LFoo := nil; //Depending on your logic, this could also be done inside TTask.Run
end;    

Sharing DataModules between VCL and FMX projects

When you start implementing cross-platform applications in Delphi’s FireMonkey framework, then you might consider reusing TDataMdoule instances from your existing VCL applications. If your code is clean enough, i.e. your DataModules are separated from any UI code, that is contain no references to WinApi, Forms, Controls or other UI relevant units, then this is actually a reasonable approach.

You might run into a weird problem though: The IDE will sometimes insist on adding VCL-dependant units to your DataModul, even if you actually compile for FireMonkey. One example is FireDAC.VCL.Wait which will get added automatically under certain conditions (and which won’t compile under FMX).

There is a property that controls this behaviour in shared TDataModule instances:

ClassGroup is basically a „Pseudo Property“, which adds some magic to the project so that the IDE will behave differently depending on the properties value. To make your DataModule really platform independent set it to:   System.Classes.TPersistent

That way, the IDE stops inserting references to either FMX or VCL units in the DataModule’s uses clause. In the case of FireDAC applications, it will just add FireDAC.ConsoleUI.Wait, which will compile just fine on all platforms. You would then add the actual Wait unit in the app’s main form (or in the DPR).

Wir benutzen Cookies um die Nutzerfreundlichkeit der Webseite zu verbessen. Durch Deinen Besuch stimmst Du dem zu.