2011-10-17 11 views
8

非常に非常に高速で、RTTIやXMLを使用しなければならない限られた数のクラス、つまりインスタンスの数が2000+以上になる可能性があるため、元に戻す+ネストされたオブジェクトリストオブジェクトは、メモのパターンを介してコピーされ、すぐに再読み込みされる必要があります。オブジェクトをメモリにコピーして複製できますか?

メモリをコピーしてそのメモリからオブジェクトを再インスタンス化することによってオブジェクトを複製する方法はありますか?

+1

オブジェクトを複製せずにメモのパターンを実装することはできませんか?すべてのオブジェクトがTPersistentから継承されているので、TPersistent.Assignをサポートするのが最善でしょうか? –

+0

オブジェクトに参照が含まれていない限り、これを行うことができます。 –

+1

問題は、オブジェクトがそのメモリを超えていることです。オープンファイル、その他のハンドル、同期プリミティブ、refcountingなどのインターフェースはどうですか?オブジェクト自体は、自分自身を正しくコピーする方法を知っています。 – haimg

答えて

15

ほとんどありません。オブジェクトのメモリを簡単にコピーすることはできますが、そのメモリの一部はポインタになります。この場合、参照のみをコピーします。これらのポインタには、文字列やその他のオブジェクトも含めることができます。

私はこれらのクラスをTPersistent(または任意のdecent)から継承し、それぞれのAssignメソッドを実装することをお勧めします。これにより、2番目のインスタンスを作成し、その新しいインスタンスにオブジェクトを割り当てることができます。 Assign実装では、コピーするデータとコピーしないデータを自分で決めることができます。

3

オブジェクトのクローンを作成する簡単な方法は次のとおりです。

  • 割り当て手順を実装TPersistentは
  • からあなたのクローニング可能なクラスを派生し
ここ

参照メメントデザインパターンの例: http://sourcemaking.com/design_patterns/memento/delphi/1

+0

10ポイントはsourcemaking.comに言及しています – MX4399

+0

thx!ソースメイキングは常に良い基準です。 – TridenT

0

TMemoryStreamを使ってUndo/Redoを実装することができます。オブジェクトのデータをストリームに保存し、REDO/UNDOが表示されたときにロードするだけです。オブジェクトのsaveToStream/loadfromstream機構を使用し、参照を正しく再構成することができます。

更新:次に

TMyObject = class 
    procedure SaveToStream(AStream : TStream); 
    procedure LoadFromStream(AStream : TStream); 
end; 

:変更前

myobject.SaveToStream(MemoryStream); 
undoManager.AddItem(myobject.Identifier, MemoryStream); 
Do change object... 

復元

myobject := FindObject(undomanager.LastItem.Identifier) 
myobject.LoadFromStream(undomanager.LastItem.MemoryStream); 

ことで、私は私のCADソフトウェアのための同様のアプローチを使用し、それはかなり動作します良い。 1つの操作で変更された複数のオブジェクトを格納するためにも使用できます。

+2

TObjectにはSaveToStreamがありません。 –

+0

私はTObjectを意味するわけではありませんでした。 – George

+1

あなたは何をしているのですか?より正確に答えを編集してください。あなたは* Object *を文の途中で大文字にして、あなたが 'TObject'を意味していたことを示唆しています。適切な名詞であることを意味していない場合は、その前に記事(* an * or * the *)も必要です。 –

10

2000+ネストされたオブジェクトはそれほど大きくなく、RTTI(ディスクアクセスまたは圧縮ははるかに遅くなります)でも非常に遅くはありません。直接SaveToStream手動シリアル化では、FastMM4(Delphi 2006以降のデフォルトのメモリマネージャ)を使用すると非常に高速です。

アルゴリズムを変更して、代わりにダイナミックアレイを使用することもできます(オープンソースシリアライザhereがあります)。しかし、あなたのデータはこの種のレコードには適合しないかもしれません。

メモリを解放することは決してありません。オブジェクトまたはレコード参照のみを使用することは決してありません。いくつかの種類の手作業のガベージコレクタを使用してオブジェクトのメモリプールを作成し、オブジェクト(またはレコード)への参照のみを処理できます。

オブジェクト内にstringがある場合、それらを再割り当てせずに、使用されている文字列のグローバルリストを維持することはできません。参照カウントとコピーオンライトにより、標準シリアル化と割り当て(FastMM4の場合も)よりもはるかに高速になります。

すべての場合、アプリケーションの実際のプロファイリングを行う価値があります。パフォーマンスのボトルネックに関する一般的な人間の推測はほとんどの場合間違っています。プロファイラーとウォールクロックだけを信頼してください。おそらくあなたの現在の実装はあまり遅くないし、実際のボトルネックはオブジェクトプロセスではなく、どこか他の場所であるかもしれません。

あまりに早く最適化しないでください。あなたが速くする前に、それをもっと速くしてください。 - KernighanとPlauger、プログラミングスタイルの要素。

+1

KernighanとPlaugerの引用の+1。 –

+0

洞察に感謝します。クローンと割り当てアプローチを使用することは、あなたが指摘したように非常に高速なメモリマネージャーに直接依存しています。重要な部分は、メモリマネージャーに焦点を当て、選択的にクローンを作成してクローンとしてフラグを立ててインテリジェントに活用します内部的にRTTIを回避する。 – MX4399

+0

@ MX4399また、グローバルな 'string'リストを試してみる価値があります:参照カウントを使用することは、ストリームアプローチよりもはるかに高速かもしれません。もちろん、 'Assign()'アプローチは、全く同じ参照カウントメカニズムの利点を利用します。 –

1

過去にこの状況で使用してきたアプローチは、保持する必要のあるオブジェクトの部分を持つレコードを宣言し、そのレコードをクラス内で使用することです。例については私の古い答え、Optimizing Class Size in Delphi. Is there something like "packed classes"?を参照してください。

レコードは割り当て時にコピーされるため、あるオブジェクトタイプから別のオブジェクトタイプにフィールドを簡単にコピーできます。

もちろん

TMyFields = record 
    ID: Integer; 
    Name: string; 
end; 

TMyClass = class(TPersistent)  
private 
    FFields: TMyFields; 
    FStack: TStack; 
    class TStackItem = class(TObject) 
    public 
    Fields: TMyFields; 
    end; 

protected 
    property ID: integer read FFields.ID; 
    property Name: string read FFields.Name; 
    procedure Push; 
    // etc... 
end; 


procedure TMyClass.Push; 
var 
    NewItem: TStackItem; 
begin 
    NewItem := TStackItem.Create; 
    NewItem.Fields := FFields; 
    FStack.Push(NewItem); 
end; 

あなたがXEを使用しているとして、あなたの代わりにGenerics.Collections.TObjectStackを使用することができます。

+0

非常に面白いアプローチ – MX4399

関連する問題