2010-02-20 7 views
4

J'ai deux unités unitA et unitB. La classe TFoo est déclarée dans l'unitéB.Delphi et finalisation dans une unité

Est-il toujours sûr d'appeler B.Free lors de la finalisation de l'unitéA?

En quoi dépend-elle dans quel ordre unitA et unitB sont en dpr? Puis-je être sûr que unitB existe quand la finalisation unitA est exécutée?

unit unitB; 
interface 
type 
TFoo = class 
    // code... 
    end; 
// code.... 
end; 

unit unitA; 
// code.. 
implementation 
uses 
unitB; 
var 
A: TStringList; 
B: UnitB.TFoo; 

initialization 
A:= TStringList.Create; 
B:= UnitB.TFoo.Create; 
finalization 
A.Free; 
B.Free; // Is it safe to call? 
end. 

Répondre

15

Oui, ça devrait aller puisque B est créé dans l'unité A. La règle est que les sections d'initialisation sont appelées selon leur ordre dans le DPR, à moins que l'une des unités ne fasse référence à une autre unité. Dans ce cas, l'unité référencée est initialisée en premier. La finalisation est dans l'ordre inverse.

Dans votre cas, l'unité B n'a pas de section d'initialisation, c'est donc un point discutable. Il utilisera cependant la définition TFoo dans l'unité B lorsque la section d'initialisation de l'unité A est exécutée.

Un autre mot d'avertissement à propos des sections Initialisation et Finalisation - elles se trouvent en dehors du gestionnaire d'exceptions global. Toute exception qui se produit là s'arrête juste l'application. Ainsi, le suivi et le débogage de ces exceptions peuvent être pénibles dans les grands programmes. Vous pourriez envisager d'utiliser votre propre enregistrement d'exception, juste pour être sûr.

+2

a voté pour le «gestionnaire d'exception globale» note – frogb

+0

Voir mon commentaire. Delhi ne garantit pas l'ordre. Votre note d'exception est vraie. – DiGi

+0

Il est intéressant de noter que Madexcept récupère les exceptions soulevées dans les sections de finalisation. –

3

Dans la mesure où je comprends ce que vous avez devrait être parfaitement valide. Un peu gênant mais valide. Mais une meilleure façon pourrait être de déclarer une variable dans l'unité B et de l'initialiser/finaliser par B. Puisque les initialisations se produisent avant que tout autre code ne soit appelé, elles seront initialisées avant d'être mises à la disposition de l'unité A tant qu'elles sont déclarées dans la clause uses de l'unité A.

Une autre étape que vous pourriez envisager est de prendre la variable unité de B un peu plus loin et l'avoir comme une fonction d'appel pour le chargement à la demande, mais cela pourrait aussi dépendre de votre utilisation.

par exemple

unit unitB; 
interface 
type 
TFoo = class 
    // code... 
    end; 
// code.... 
function UnitVarB:TFoo; 

implementation 

var 
    gUnitVarB : TFoo; 

function UnitVarB:TFoo 
begin 
    if not assigned(gUnitVarB) then 
    gUnitVarB := TFoo.Create; 

    result := gUnitVarB; 
end; 

finalization 
    if assigned(gUnitVarB) then 
    gUnitVarB.free; //or FreeAndNil(gUnitVarB); 

end; 

unit unitA; 

// code.. 
implementation 

uses 
unitB; 

var 
A: TStringList; 

//code... 
    ...UnitVarB.... 
//code... 

initialization 
A:= TStringList.Create; 
finalization 
A.Free; 
end. 

me semble me rappeler quelque part que l'unité initialisations pourrait être coûteux en ce que si une unité que vous ne plus directement référence est toujours dans votre clause uses au cours d'une compilation, l'éditeur de liens intelligent ne sera pas retirez-le en raison de la section d'initialisation. Bien que cela puisse ne pas sembler si mauvais si chaque unité avait une section d'initialisation alors la plupart des programmes Delphi seraient MUCH plus grand qu'ils ne le sont déjà. Je ne dis pas ne pas les utiliser mais ma règle générale est de les utiliser avec parcimonie.

Votre exemple de code initial rompt cette règle. Je pensais que je le mentionnerais.

Ryan

+0

Merci. La fonction UnitVarB est une manière intelligente d'éviter les variables globales. – pKarelian

+0

J'utilise le nettoyant pour utilisations de CnPack. Il semble sauter les unités qui ont l'initialisation. – pKarelian

1

Oui, c'est sûr. Vous pouvez simplifier le travail du compilateur en déclarant UnitB avant UnitA dans le fichier dpr, mais le compilateur résoudra les références dans tous les cas.

1

Dans l'esprit de la divulgation complète, je n'ai pas développé en Delphi depuis 2005. Cependant, j'ai développé en Delphi exclusivement à partir de Delphi 1 en 1996, et a été certifié en Delphi 5 en 2001. Cela étant dit, mon utilisation de la section de finalisation était rare. La seule fois où je l'utiliserais c'est si j'avais besoin de mettre quelque chose de spécial dans le fichier .dpr. Cela ne se produisait généralement que si je faisais du développement de composants personnalisés, ET il y avait quelques dépendances que j'avais besoin de gérer en utilisant d'autres composants personnalisés que je développais. Pour le développement d'applications typiques, je suis resté loin de la section d'initialisation/finalisation et j'ai juste utilisé des modèles de conception comme des singletons, des façades et des usines pour gérer la création et la gestion de mes classes. Le garbage collector intégré était assez bon pour 98,5% de mes projets.

Pour répondre à votre question, vous devez configurer une dépendance à TFoo dans votre code UnitA, et, comme Ryan l'a suggéré, assurez-vous qu'il est assigné avant la destruction. Cela étant dit, je vous encourage à vous assurer que l'utilisation de la section d'initialisation/finalisation est nécessaire avant d'y consacrer trop de temps.

+1

Delphi n'a pas de garbage collector. –

+2

L'affiche fait probablement référence au comportement VCL "Owner" et/ou au fait que la mémoire pour les objets qui ne sont pas explicitement Free'd est renvoyée au système lorsque le processus se termine, bien que leurs destructeurs ne s'exécutent pas. – Deltics

+1

Jim: Merci de me le rappeler ... apparemment, l'exorcisme a été un succès complet. : o) Deltics: Je me souviens avoir souvent utilisé FreeAndNil pour libérer la mémoire et le pointeur dans mon code pour les objets que j'ai créés dans le code, mais je n'utiliserais pas la section d'initialisation sauf si je travaillais avec une ressource gérée par le système, comme un objet COM. Même alors, il était généralement associé à des bibliothèques dépendantes ou en s'assurant que les ressources étaient disponibles au démarrage de mon composant. –

4

NO. Vous pouvez essayer, vous pouvez espérer mais il y a aucune garantie dans l'ordre d'initialisation d'appel et la finalisation. Voir qc72245, qc56034 et many more.

UPDATE:

  1. section de finalisation est exécutée dans l'ordre inverse de l'initialisation. Votre exemple est sûr, vous n'avez pas la dépendance à appeler des sections d'initialisation entre les unités
  2. Delphi peut mélanger des unités composant le (point 1 est toujours valable, à la fois l'initialisation et les sections de finalisation sont permutés)

Exemple:

unitA // no dependency on unitB 
var SomeController; 
initialization 
    SomeController := TSomeController.Create; 
finalization 
    SomeController.Free; 

unitB 
uses 
    unitA; 
initialization 
    SomeController.AddComponent(UnitBClass); 
finalization 
    SomeController.RemoveComponent(UnitBClass); 

ordre commun (correct) (99,99%) d'appeler:

  1. unitA.initialization
  2. unitB.initialization
  3. ...
  4. run
  5. unitB.finalization
  6. unitA.finalization

Mais parfois peut Delphi compiler mal de fichier:

  1. unitB.initialization - AV ici
  2. unitéA.initialisation
  3. ...
  4. run
  5. unitA.finalization
  6. unitB.finalization - et ici aussi

Petite histoire offtopic:

Nous avons assez grand projet, avec Type1 dans part1, type2 = classe (Type1) dans Unit2. Les fichiers sont classés dans le projet.dpr et après des années et en ajoutant Unit200 (aucune dépendance avec unit1/2) Delphi commence la compilation du projet avec Unit2.Initialization avant Unit1.Initialization. Seule une solution sûre appelle vos propres fonctions Init à partir de la section d'initialisation.

+0

La question initiale ne concerne pas l'ordre d'initialisation/finalisation. – kludg

+0

Même si l'unité B est finalisée en premier, cela ne provoque pas l'arrêt de l'unité * existante *. Le code qui utilise les choses définies dans l'unité B continuera à fonctionner. –

+2

Mais cela dépend. ** finalisation ** les pièces ** sont ** exécutées dans l'ordre inverse de l'initialisation, mais Delphi peut échanger des unités et commander – DiGi

3

Dans la situation spécifique que vous montrez ici, tout ira bien. Mais il ne faudrait pas beaucoup de refactoring avant de commencer à mal tourner. Delphi fait du bon travail en s'assurant que les unités restent en mémoire aussi longtemps que nécessaire. Mais il ne peut le faire que s'il sait qu'une unité est nécessaire.

Mon exemple classique sur le sujet est une unité contenant rien d'autre qu'un ObjectList

unit Unit1; 

interface 
uses 
    Contnrs; 
var 
    FList : TObjectList; 
implementation 

initialization 
    FList := TObjectList.Create(True); 
finalization 
    FList.Free; 
end. 

part1 ne dépend explicitement Contnrs. Delphi s'assurera seulement que l'unité Contnrs (et probablement aussi les unités "subordonnées", bien que je ne sois pas sûr à 100%) soit encore chargée en mémoire. Si un TForm est ajouté dans la liste, l'unité Forms peut être déjà finalisée lorsque FList.free est appelé, il va planter quand il tente de libérer le TForm qu'il contient. Delphi n'a aucun moyen de savoir que Unit1 nécessite l'unité Forms. Dans ce cas précis, cela dépendra des unités de commande déclarées dans le dpr.

Questions connexes