Unit-Initialisierung und -Finalisierung für die neue C++-Toolkette
Nach oben zu Packages für die neue C++-Toolkette
Abhängigkeiten bei der Initialisierung und Finalisierung von Units können zu Problemen führen. Da dies ein wiederkehrendes Problem sein kann, wurden für RAD Studio erhebliche Anstrengungen unternommen, um die Funktionsweise von Packages zu verbessern und ein einheitliches Verhalten beim statischen und dynamischen Linken von Packages sicherzustellen. In diesem Artikel wird beides erklärt.
Packages sind immer in zwei Versionen verfügbar, wobei die zweite als statische Standardbibliothek vorliegt. Beim statischen Linken eines Package wird es mit dieser statischen Standardbibliothek gelinkt. Das bedeutet, der Package-Inhalt wird in die Binärdatei übernommen, die Sie linken. Für das Package müssen keine Dateien weitergegeben werden.
In Delphi haben Units gegenseitige Abhängigkeiten. Das bedeutet, dass eine Unit eine andere Unit und die Typen aus dieser Unit verwendet. Abhängigkeiten bei der Initialisierung und Finalisierung von Units können vorkommen, wenn eine Unit initialisiert wird. Das heißt, wenn eine der folgenden Optionen verwendet wird:
- Die Schlüsselwörter initialization und finalization
- Klassenkonstruktoren oder -destruktoren
Beispielsweise bei der Initialisierung eines Objekts, das von einer anderen Objektinstanz oder -zuweisung in einer anderen Unit abhängig ist. Dieses Verhalten führt dazu, dass Units auf Grundlage von Abhängigkeiten in der Graphenzuordnung initialisiert werden. Es gibt nicht unbedingt eine richtige Lösung für einen Abhängigkeitsgraphen, der zirkulär sein kann. Wenn dem so ist, dann werden wahrscheinlich Fehler auftreten.
Initialisierungsprobleme können z. B. häufig AVs beim Start oder beim Laden eines Package verursachen, hauptsächlich wenn sie von einer Unit abhängig sind, die noch nicht initialisiert wurde. In RAD Studio wurde versucht, diese Probleme zu vermeiden oder zu verringern.
Beachten Sie, dass sowohl beim dynamischen als auch beim statischen Linken nur die verwendeten Units initialisiert und finalisiert werden. Im Gegensatz zu früheren Toolketten (Clang 5 und früher und klassisch) werden zirkuläre Unit-Referenzen behandelt und schwach gepackte Units werden korrekt verarbeitet.
Wie werden Packages dynamisch initialisiert?
Wenn ein Package dynamisch geladen wird, werden seine Units rekursiv mit einem Zähler initialisiert. Dadurch wird sichergestellt, dass ein zweites Package, das dieselben Units verwendet, nicht doppelt initialisiert wird und erst dann finalisiert wird, wenn das letzte Package entladen wurde.
Packages können auf zwei Arten dynamisch initialisiert werden:
-
Über die Package-Importbibliothek, die
.BPI-Datei. Dies ist das übliche Verfahren, und so funktioniert das automatische Linken.
Diese Methode verwendet das Record-Initialisierungssystem und initialisiert konsequent nur die verwendeten Units. Dies kann zu einer unterschiedlichen Reihenfolge führen, in der Units initialisiert werden, wenn ein Package von zwei Stellen in derselben Anwendung abhängig ist.
Lassen Sie uns das anhand eines Beispiels erläutern. Nehmen wir folgendes Szenario an:-
App.exehängt vonA.bplundB.bplab. -
B.bplhängt von onA.bplab.
Wenn
App.exegeladen wird, werden die Units initialisiert, die sie ausA.bplsowie aus allen weiteren Units verwendet, von denen die Units selbst abhängig sind. Das bedeutet, der gesamte Unit-Abhängigkeitsgraph wird initialisiert.
Die Units in
A.bpl, von denenB.bplabhängig ist, werden jedoch nicht initialisiert. WennApp.exeB.bpllädt, werden ihre Abhängigkeiten ausA.bplinitialisiert. Wenn eine davon Units inA.bpleinschließt, die bereits initialisiert wurden, werden diese Units nicht zweimal initialisiert.
Das bedeutet, dass zwar jeder Graph bzw. jede Gruppe von Units korrekt initialisiert wird, es jedoch denkbar ist, dass durch das Laden von
A.bpldurchApp.exevorB.bpleine andere Initialisierungsreihenfolge der Units von A verursacht werden kann, als wennApp.exenur vonB.bplabhängig wäre oder in der ReihenfolgeB.bpl(->A.bpl) dannA.bplgeladen worden wäre. Dies sollte keinerlei Auswirkungen auf die Stabilität haben, da alle verwendeten Units in der richtigen Reihenfolge initialisiert werden sollten, wie in den Initialisierungs-Records angegeben. -
- Über SysUtils::LoadPackage. Mit dieser Methode werden Packages vollständig dynamisch geladen.
Alle abhängigen Package-Units werden rekursiv initialisiert, ob sie verwendet werden oder nicht. Und zwar deshalb, weil nicht bekannt ist, welche das ladende Modul aus dem Package verwendet.
Wenn ein Package geladen wird, werden auch seine Abhängigkeiten geladen. Das bedeutet: Wenn Sie ein Package namensPackage1.bplladen, das vonrtl.bplabhängt – sei es durch dynamisches Linken oder mithilfe vonLoadPackage–, müssen weder Sie nochPackage1.bplLoadPackage(“rtlXXX.bpl”)aufrufen. Das liegt daran, dass beim Laden vonPackage1.bplalle abhängigen Package-Units, diePackage1.bplverwendet, ebenfalls geladen und initialisiert werden, wozu auch die Behandlung von Abhängigkeiten zu Units in anderen Packages gehört, wie beispielsweisertl.bpl.
In einer Anwendung können die beiden Methoden zum dynamischen Laden eines Package kombiniert werden, aber statisches und dynamisches Linken sollten nicht miteinander vermischt werden.
Unit-Initialisierung und -Finalisierung sind naturgemäß komplexer, da es mehr Abhängigkeitsebenen als in einem Package und zwischen Packages gibt. Gute Tests prüfen komplexe Abhängigkeiten.
Beim statischen Linken werden nur die verwendeten Units initialisiert. Der Linker entfernt nicht verwendete Objekte. D. h. Units, auf die nicht verwiesen wird oder die nicht verwendet werden, sind in der endgültigen Binärdatei nicht enthalten.