Delphiの新しいバージョンの弱いリンク

こんにちは。



Embarcaderoは昨日、 Delphi RAD studio XE 10.1の新しいバージョンのリリースを発表しました。

ここで変更点の全リストを見ることができますが、最も価値のある(当社にとっての)改善点、つまりクラシックコンパイラ(Win32 / Win64)での弱いリンクの導入についてお話したいと思います



上記の記事で問題の詳細が示されているので、Delphiで何をしたかを見たい人のために、猫をお願いします。



インターフェースコンテナとして弱いリンクを使用する例を見てみましょう。 もちろん、他の状況では弱いリンクが必要ですが、この特定のケースを検討します。 このようなコレクションには、コンテナ要素に所有者コレクションへのリンクが必要です(たとえば、要素へのリンクを設定するには、コレクションから削除するか、単にアクセスするだけです)。 次に、コレクション自体にも要素へのリンクを保存する必要があります。 Delphiの以前のバージョンでは、通常のリンク(強力な参照)のみを宣言する機会があったため、このコレクションで循環リンクを受け取ったため、メモリを正しく解放できませんでした。 通常、この状況から抜け出す方法は2つあります。

1.要素クラスは、FOwnerフィールドに型指定されていないポインターを使用しました。 このようなポインターにインターフェイスリンクを割り当てても参照カウンターは増加しませんでしたが、循環リンクの問題は解決しましたが、このコードは安全ではありません。コレクション-所有者。 ポインタFOwner-既に削除されたメモリをポイントし続けました。

2.前の方法の問題を解決するために、アイテムのアラートを実装してコレクションを削除できます。 削除するとき、コレクションはそのすべての要素を実行し、特定のメソッドを呼び出します。これにより、FOwnerフィールドが「下げられます」。 このアプローチの欠点は、追加のコードとそれを実行するのに必要な時間です。 コレクションの例の場合、これはそれほど怖いわけではありませんが、他の重要な例では、このコードはロジックを非常に混乱させ、エラーを追加する可能性があります。

そして現在、XE10.1ベルリンの最新バージョンでは、この問題を解決する人間にとって通常の機会が現れました。 必要なのは、[弱い]属性を持つFOwnerフィールドを宣言することだけです。 そのようなリンクは依然として強く型付けされますが、ソースオブジェクトの参照カウントを増加させない割り当てです。 オブジェクト(この場合はコレクションの所有者)がメモリから削除された場合、そのようなリンクは自動的に「控えめ」になり、オブジェクトが削除されたことを示します。



2つのインターフェイスと、これらのインターフェイスを実装する2つのクラスを作成します(この例は制限まで簡略化されていますが、速度の最適化については質問がありませんでした)

インターフェイスとクラスの説明
type { } IXListItem = interface ['{02E680D6-9F86-4303-85B5-256ACD89AD46}'] function GetName: string; procedure SetName(const Name: string); property Name: string read GetName write SetName; end; { } IXList = interface ['{922BDB26-4728-46DA-8632-C4F331C5A013}'] function Add: IXListItem; function Count: Integer; function GetItem(Index: Integer): IXListItem; property Items[Index: Integer]: IXListItem read GetItem; procedure Clear; end; {  } TXListItem = class(TInterfacedObject, IXListItem) private [weak] FOwner: IXList; //     FName: string; public constructor Create(const Owner: IXList); destructor Destroy; override; procedure SetName(const Name: string); function GetName: string; end; {  } TXList = class(TInterfacedObject, IXList) private FItems: array of IXListItem; public function Add: IXListItem; function Count: Integer; function GetItem(Index: Integer): IXListItem; procedure Clear; destructor Destroy; override; end;
      
      







TXListItemオブジェクトを見るとわかるように、所有者オブジェクトへの弱い参照があります。



TXListItemクラスの実装、GetNameメソッドでウィークリンクを使用します。

TXListItem
 {TXListItem} constructor TXListItem.Create(const Owner: IXList); begin FOwner := Owner; //    end; destructor TXListItem.Destroy; begin ShowMessage('Destroy ' + GetName); inherited; end; function TXListItem.GetName: string; var List: IXList; begin List := FOwner; if Assigned(List) then Exit(FName + '; owner assined!') else Exit(FName + '; owner NOT assined!'); end; procedure TXListItem.SetName(const Name: string); begin FName := Name; end;
      
      







TXListクラスは一般的です:

Txlist
 { TXList } function TXList.Add: IXListItem; var c: Integer; begin c := Length(FItems); SetLength(FItems, c + 1); Result := TXListItem.Create(Self); FItems[c] := Result; end; procedure TXList.Clear; var i: Integer; begin for i := 0 to Length(FItems) - 1 do FItems[i] := nil; end; function TXList.Count: Integer; begin Result := Length(FItems); end; destructor TXList.Destroy; begin Clear; ShowMessage('Destroy list'); inherited; end; function TXList.GetItem(Index: Integer): IXListItem; begin Result := FItems[Index]; end;
      
      







変数を宣言します。

var
 var Form1: TForm1; List: IXList; Item: IXListItem;
      
      







そして、オブジェクトを作成および削除するためのメソッド。

方法
 procedure TForm1.btnListCreateAndFillClick(Sender: TObject); begin List := TXList.Create; //   List.Add.Name := 'item1'; //    List.Add.Name := 'item2'; Item := List.Add; //       Item.Name := 'item3'; end; procedure TForm1.btnListClearClick(Sender: TObject); begin List := nil; //   end; procedure TForm1.btnLastItemFreeClick(Sender: TObject); begin Item := nil; //   ,    end; procedure TForm1.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := true; //     end;
      
      







[weak]リンク宣言の使用例:

画像



[弱い]リンクを宣言せずに作業する例:





ご覧のとおり、[weak]リンクを使用せずに、コレクションと要素は相互に周期的に参照し、デストラクタを呼び出さないため、FastMMが伝えるメモリリークが発生します。



また、[弱点]属性は[弱点]に類似した新しいバージョンに登場しましたが、それが参照するオブジェクトを削除しても、リンクは自動的に「失敗」しません。

ちなみに、すでに1つのバグが見つかり、それをqcに送信しました



h_xandrの同僚の例とヘルプの執筆に感謝します



更新しました。 リポジトリリンク



All Articles