I need to have undo+redo stack for a limited number of classes under my control that has to be very very very fast and using RTTI and XML or streams is not feasible as the count of instances can be as high as 2000+ in nested object lists. The objects need to be copied into and out of via memento pattern and reloaded instantly.
Is there a way to clone objects by copying the memory and re-instantiating the objects from that memory?
There are several ways to copy an object, most commonly by a copy constructor or cloning. Copying is done mostly so the copy can be modified or moved, or the current value preserved. If either of these is unneeded, a reference to the original data is sufficient and more efficient, as no copying occurs.
Map clonedObject = JSON. decode(JSON. encode(object)); If you're using a custom class as a value in the object to clone, the class either needs to implement a toJson() method or you have to provide a toEncodable function for the JSON.
Hardly. You can easily copy the memory of an object, but part of that memory will be pointers in which case you only copy the reference. Those pointers can include strings and other objects as well.
I think the best way is to inherit these classes from TPersistent (or any decendant) en implement the Assign method for each of them. That way, you can create a second instance and assign the object to that new instance. In the Assign implementation you can decide for yourself what data should be copied and what not.
2000+ nested objects is not so huge, and won't be so slow, even with RTTI (disk access or compression will be much slower). With a direct SaveToStream manual serialization, it is very fast, if you use FastMM4 (the default memory manager since Delphi 2006).
Perhaps you may change your algorithm and use dynamic arrays instead (there is an open source serializer here). But your data may not fit this kind of records.
Or never/seldom release memory, and only use objects or record references. You can have an in-memory pool of objects, with some kind of manual garbage collector, and only handle reference to objects (or records).
If you have string
inside the objects, you may not reallocate them and maintain a global list of used strings: reference counting and copy-on-write will make it much faster than standard serialization and allocation (even with FastMM4).
In all cases, it is worth making a real profiling of your application. General human guess about performance bottlenecks are most of the time wrong. Only trust a profiler, and a wall clock. Perhaps your current implementation is not so slow, and the real bottleneck is not the object process, but somewhere else.
Do not optimize too early. "Make it right before you make it fast. Make it clear before you make it faster. Keep it right when you make it faster." — Kernighan and Plauger, Elements of Programming Style.
A simple way to clone an object is :
See Memento design pattern example here : http://sourcemaking.com/design_patterns/memento/delphi/1
An approach I have used for this situation in the past is to declare a record with the part of the object I need to retain, and use that record within a class. See my old answer at Optimizing Class Size in Delphi. Is there something like "packed classes"? for an example.
As records are copied on assignment, it is then easy to copy the fields from one object type to another.
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;
Of course, as you are using XE, you could use Generics.Collections.TObjectStack instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With