4

unitA と unitB の 2 つのユニットがあります。クラス TFoo は unitB で宣言されています。

unitA のファイナライズで B.Free を呼び出すことは常に安全ですか?

unitA と unitB が dpr にある順序にどのように依存しますか?

unitA ファイナライズが実行されたときに unitB が存在することを確認できますか?

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.
4

6 に答える 6

16

はい、B はユニット A で作成されているので問題ありません。ユニットの 1 つが別のユニットを参照しない限り、初期化セクションは DPR 内の順序に基づいて呼び出されるという規則があります。その場合、参照されるユニットが最初に初期化されます。ファイナライズは逆順です。

あなたの場合、ユニット B には初期化セクションがないため、問題点です。ただし、ユニット A の初期化セクションが実行されると、ユニット B の TFoo 定義が使用されます。

Initialization セクションと Finalization セクションに関するもう 1 つの警告 - これらはグローバル例外ハンドラの外で発生します。そこで例外が発生すると、アプリケーションが終了します。したがって、これらの例外を追跡してデバッグすることは、大規模なプログラムでは苦痛になる可能性があります。念のため、独自の例外ログを使用することを検討してください。

于 2010-02-20T07:26:07.957 に答える
4

番号。試すことはできますが、初期化とファイナライズを呼び出す順序に保証はありません。qc72245qc56034などを参照してください

アップデート:

  1. ファイナライズ セクションは、初期化とは逆の順序で実行されます。あなたの例は安全です。ユニット間の初期化セクションの呼び出しに依存していません
  2. Delphi は呼び出しユニットを混在させることができます(ポイント 1 は引き続き有効で、初期化セクションとファイナライズ セクションの両方が交換されます)

例:

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

呼び出しの一般的な (正しい) 順序 (99.99%):

  1. unitA.初期化
  2. unitB.初期化
  3. 走る...
  4. unitB.ファイナライズ
  5. unitA.ファイナライズ

しかし、Delphi がファイルを間違ってコンパイルすることがあります。

  1. unitB.initialization - ここで AV
  2. unitA.初期化
  3. 走る...
  4. unitA.ファイナライズ
  5. unitB.finalization - ここでも

少しオフトピックの話:

Unit1 に Type1、Unit2 に Type2 = class(Type1) という非常に大きなプロジェクトがあります。ファイルは project.dpr で順序付けされ、数年後に Unit200 を追加します ( unit1/2 との依存関係はありません) Delphi は、Unit1.Initialization の前に Unit2.Initialization でプロジェクトのコンパイルを開始します。安全な解決策は、初期化セクションから独自の Init 関数を呼び出すことだけです。

于 2010-02-20T10:42:26.717 に答える
3

ここに示されている特定の状況では、問題ありません。しかし、うまくいかなくなる前に、それほど多くのリファクタリングは必要ありません。

Delphi は、ユニットが必要な限りメモリ内にとどまるように、非常に優れた機能を果たします。ただし、ユニットが必要であることがわかっている場合にのみ、そうすることができます。

このトピックに関する私の古典的な例は、オブジェクトリストだけを含むユニットです

unit Unit1;

interface
uses
  Contnrs;
var
  FList : TObjectList;
implementation

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

Unit1 は Contnrs に明示的に依存するだけです。Delphi は、Contnrs ユニット (および、100% 確実ではありませんが、おそらく「従属」ユニットも) がまだメモリにロードされていることを確認するだけです。TForm がリストに追加されている場合、Forms ユニットFList.freeは呼び出された時点で既にファイナライズされている可能性があり、含まれている TForm を解放しようとするとクラッシュします。Delphi は、Unit1 に Forms ユニットが必要であることを知る方法がありません。この特定のケースでは、ユニットが dpr で宣言されている順序に依存します。

于 2014-11-21T21:25:19.857 に答える
3

私が理解している限り、あなたが持っているものは完全に有効でなければなりません。少し厄介ですが有効です。

しかし、より良い方法は、ユニット B で変数を宣言し、B に初期化/ファイナライズさせることです。初期化は他のコードが呼び出される前に行われるため、ユニット A の uses 句で宣言されている限り、ユニット A で使用できるようになる前に初期化されます。

考慮すべきもう 1 つのステップは、B の単位変数をさらに一歩進めて、オンデマンド読み込みの関数呼び出しとして使用することですが、これも使用法に依存する可能性があります。

例えば

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.

ユニットの初期化は、コンパイル中に直接参照しなくなったユニットがまだ uses 句にある場合、初期化セクションのためにスマートリンカーがそれを削除しないという点で高価になる可能性があることをどこかで覚えているようです。すべてのユニットに初期化セクションがある場合、これはそれほど悪くはないように聞こえるかもしれませんが、ほとんどの Delphi プログラムは、現在よりもはるかに大きくなります。

私はそれらを使用しないと言っているわけではありませんが、私の経験則は控えめに使用することです.

最初のコード例はそのルールを破っています。挙げておこうと思いました。

ライアン

于 2010-02-20T07:12:43.443 に答える
1

完全開示の精神で、私は 2005 年以降 Delphi で開発していません。ただし、1996 年の Delphi 1 以降は Delphi のみで開発し、2001 年に Delphi 5 で認定されました。まれでした。私がこれを使用するのは、.dpr に何か特別なものを設定する必要がある場合だけです。これは通常、カスタム コンポーネントの開発を行っている場合にのみ発生し、さらに、開発中の他のカスタム コンポーネントを使用して管理する必要のある依存関係がいくつかありました。

典型的なアプリケーション開発では、初期化/ファイナライズのセクションから離れ、シングルトン、ファサード、ファクトリなどの設計パターンを使用して、クラスの作成と管理を管理しました。組み込みのガベージ コレクタは、私のプロジェクトの 98.5% で十分でした。

あなたの質問に答えるには、UnitA コードで TFoo への依存関係を設定する必要があります。Ryan が提案したように、破棄の前にそれが割り当てられていることを確認してください。そうは言っても、時間をかけすぎる前に、初期化/終了セクションの使用が必要であることを確認することをお勧めします。

于 2010-02-20T07:25:36.553 に答える
1

はい、安全です。dpr ファイルで UnitA の前に UnitB を宣言することにより、コンパイラのジョブを簡素化できますが、コンパイラはどのような場合でも参照を解決します。

于 2010-02-20T07:21:28.340 に答える