我需要在我的控制下为有限数量的类提供undo + redo堆栈,这些类必须非常非常快并且使用RTTI和XML或流是不可行的,因为实例的数量可能高达2000+在嵌套对象列表中。需要通过memento模式复制和移出对象并立即重新加载。
有没有办法通过复制内存并从内存中重新实例化对象来克隆对象?
答案 0 :(得分:15)
几乎没有。您可以轻松复制对象的内存,但该内存的一部分将是指针,在这种情况下,您只复制引用。这些指针也可以包括字符串和其他对象。
我认为最好的方法是从TPersistent(或任何后代)继承这些类,为每个类实现Assign方法。这样,您可以创建第二个实例并将对象分配给该新实例。在Assign实现中,您可以自己决定应该复制哪些数据以及不应该复制哪些数据。
答案 1 :(得分:11)
2000+嵌套对象不是那么庞大,并且即使使用RTTI也不会那么慢(磁盘访问或压缩会慢得多)。使用直接的SaveToStream手动序列化,如果你使用FastMM4(自Delphi 2006以来的默认内存管理器),它会非常快。
也许您可以更改算法并使用动态数组(有一个开源序列化程序here)。但是你的数据可能不适合这种记录。
或者从不/很少释放内存,只使用对象或记录引用。您可以拥有一个内存中的对象池,使用某种手动垃圾收集器,并且只处理对象(或记录)的引用。
如果对象中有string
,则可能无法重新分配它们并维护已使用字符串的全局列表:引用计数和写入时复制将使其比标准序列化和分配快得多(即使使用FastMM4)。
在所有情况下,都值得对您的应用程序进行真正的分析。一般人类对性能瓶颈的猜测大多数时候都是错误的。只相信一个分析器和一个挂钟。也许你当前的实现并不是那么慢,真正的瓶颈不是对象进程,而是其他地方。
不要太早优化。 “在你加速之前做好准备。在你加快速度之前先说清楚。当你加快速度时保持正确。” - Kernighan和Plauger,编程风格的元素。
答案 2 :(得分:3)
克隆对象的一种简单方法是:
请参阅此处的Memento设计模式示例: http://sourcemaking.com/design_patterns/memento/delphi/1
答案 3 :(得分:1)
我过去用于此情况的方法是使用我需要保留的对象部分声明记录,并在类中使用该记录。请参阅Optimizing Class Size in Delphi. Is there something like "packed classes"?上的旧答案。
当记录在赋值时被复制时,可以很容易地将字段从一种对象类型复制到另一种对象类型。
e.g。
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。
答案 4 :(得分:0)
可以使用TMemoryStream实现撤消/重做 - 只需将对象的数据保存为流,并在出现重做/撤消时加载它。它使用对象的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软件使用类似的方法,它的效果非常好。它还可以用于存储多个对象,在一个操作中进行更改。