2017-11-03 10 views
6

集計を使用して複数の子オブジェクトを含むクラスTParentを作成したいとします。オブジェクトの中には独立しているものもあれば、他の子供たちに依存しているものもあります。すべての子オブジェクトは、親への参照を持たなければなりません。私はまた可能な限りインターフェイスを使いたいと思っています。Delphi:[weak]属性を使用したオブジェクトの集約とメモリリーク

私はTParentの場合はTInterfacedObject、子供の場合はTAggregatedObjectを使用しています。子供と親の両親がお互いに知っているので、私は循環の依存を避けるために弱い参照を使用しています。実際、この動作は既にTAggregatedObjectで定義されています。独立した子オブジェクト(TIndependantChild)のみを使用している場合は、すべて正常に動作します。

子オブジェクトが他の子にも依存する場合、問題が発生します。コンストラクタTDependantChildを参照してください。私は[weak]アトリビュートでマークされているfChild変数に別の子オブジェクトへの参照を格納し、Delphi 10 Berlinで導入しました。 FastMM4は、シャットダウン時にメモリリークを報告します。

enter image description here

またSystem.TMonitor.Destroy昇給につながる違反にアクセスしますが、FastMM4は用途にあり、ReportMemoryLeaksOnShutDownがTrueの場合にのみ、これは起こります。

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    FastMM4, 
    System.SysUtils; 

type 
    IParent = interface 
    ['{B11AF925-C62A-4998-855B-268937EF30FB}'] 
    end; 

    IChild = interface 
    ['{15C19A4E-3FF2-4639-8957-F28F0F44F8B4}'] 
    end; 

    TIndependantChild = class(TAggregatedObject, IChild) 
    end; 

    TDependantChild = class(TAggregatedObject, IChild) 
    private 
    [weak] fChild: IChild; 
    public 
    constructor Create(const Controller: IInterface; const AChild: IChild); reintroduce; 
    end; 

    TParent = class(TInterfacedObject, IParent) 
    private 
    fIndependantChild: TIndependantChild; 
    fDependantChild: TDependantChild; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    end; 

{ TParent } 

constructor TParent.Create; 
begin 
    fIndependantChild := TIndependantChild.Create(Self); 
    fDependantChild := TDependantChild.Create(Self, fIndependantChild); 
end; 

destructor TParent.Destroy; 
begin 
    fDependantChild.Free; 
    fIndependantChild.Free; 
    inherited; 
end; 

{ TDependantChild } 

constructor TDependantChild.Create(const Controller: IInterface; const AChild: IChild); 
begin 
    inherited Create(Controller); 
    fChild := AChild; 
end; 

var 
    Owner: IParent; 

begin 
    ReportMemoryLeaksOnShutDown := True; 
    Owner := TParent.Create; 
    Owner := nil; 
end. 

Iは、[危険]の代わりに[弱]を使用して問題を解決することが、見出されたが、デルファイに係るhelp

それ([危険])のみシステムユニットの外部で使用されるべきです非常にまれな状況です。

したがって、私は確信していません。私は[unsafe]をここで使用する必要があります。特に何が起こっているのかわからないときは。

このような状況でのメモリリークの根拠とその克服方法を教えてください。

+1

子供はなぜ集計する必要がありますか?集約が実際に何であるか理解していますか?なぜオブジェクト参照とインターフェイスを混在させるのですか?それは常に災害のレシピです。デバッガを使用して、メモリが漏れている理由を確認します。 –

+1

10.1 Berlinのインタフェースに '[weak]'が追加されましたが、 '[weak]'は以前のバージョンに存在します。私はあなたのコードをそのままXE2でコンパイルできますが、 '[weak]'は効果がありません。 'Owner 'は、' TDependantChild'が '[weak]'であるにもかかわらず' fChild'が割り当てられているとき(集約のために) 'TParent'への非弱参照を持っているので' RefCount = 1'を持ちます。 'Owner'が破壊されると、' TInterfacedObject.BeforeDestruction() 'は' RefCount <> 0'のときにエラーを発生させ、 'Owner'とその子をリークさせます。 'fChild'を' Pointer'に変更するとそれが修正されます。あなたが ''安全でない ''を使用している場合、私はあなたの場合に似ていると思われます。デバッガで確認 –

+0

レミー、コメントありがとうございます。私はそれが間違っていると理解していたかもしれませんが、集約コンセプトには新しいものです。私の場合、子オブジェクトは特定のクラス機能を表します。彼らは親なしでは意味がありませんが、同じ子オブジェクトを使って異なる親を構成することができます。私はインターフェイスとオブジェクトリファレンスを混在させたくないのですが、いくつかの例が見つかりました。[link](https://stackoverflow.com/questions/3483680/delphi-how-delegate-interface-implementation-to-child -object)、インターフェイスのみを使用するとエラーが発生しました。私はすぐに第二委員会をチェックします。 – VyPu

答えて

4

外部FastMM4メモリマネージャを使用するときのリークとクラッシュの問題は、弱参照を追跡するために使用される内部HashMapのファイナライズに関する次の問題に関連しています。

[REGRESSION XE2/10.1 Berlin] Unable to use 3rd party memory managers

その問題には、Delphi 10.1以降のバージョン、および外部FastMM4含むでリーク検出のためのサードパーティのメモリマネージャを使用することは不可能である原因。

これは、[weak]属性に問題があり、[unsafe]に問題がない理由です。


限り、あなたのコードが関係しているとして、あなたは、安全上のシナリオで[unsafe]を使用することができます。ドキュメントには[unsafe]属性の使用に関する警告がありますが、その警告は実際には[unsafe]を使用しない理由を説明していません。

[unsafe]参照で参照されているオブジェクトインスタンスの有効期間が参照自体の有効期間より長い場合、ストーリーを短くするには、[unsafe]属性を使用できます。

つまり、指し示すオブジェクトインスタンスが解放された後で、そのすべてが参照先[unsafe]にアクセスしないようにする必要があります。

[unsafe]参照は、それらが指し示すオブジェクトが破棄されたときにゼロにされず、オブジェクトがなくなった後でその参照を使用すると、アクセス違反の例外が発生します。

[weak]属性を[unsafe]に置き換えることは、提示したとおりに適切な機能コードを使用するために必要な作業です。

TDependantChild = class(TAggregatedObject, IChild) 
    private 
    [unsafe] fChild: IChild; 
+0

非常に明確な答えのために@Dalijaありがとうございます!何が起こるのか理解しようとするうちに、私は何日も過ごす。私はあなたの提供されたリンクで見つけた別の関連するディスカッションに[link](https://github.com/pleriche/FastMM4/issues/18)を追加します。 FastMM4では、このサンプルプロジェクトでは、ここで説明したのと同じアクセス違反エラーが発生します。 – VyPu

関連する問題