Eigentlich brauchst du in deinem Projekt ja gar nicht viel ändern.
Du musst nur in dem Name-Setter eine Logik einbauen, die den String cached, bzw. den String aus dem Cache verwendet.
Dann bleibt deine Anwendung wie sie ist und trotzdem verbrennt die keinen Speicher.
So könnte dann ein StringCache aussehen
Delphi-Quellcode:
unit Common.StringCache;
interface
uses
System.Generics.Collections,
System.Generics.Defaults,
System.SysUtils;
type
IStringCache =
interface
[ '
{BDD29CD1-319F-43AF-B0EE-1C7AE5E66AEC}' ]
function Get(
const AValue:
string ):
string;
procedure Reset(
var AValue:
string );
end;
TStringCache =
class( TInterfacedObject, IStringCache )
private
FLock: TObject;
FDict: TDictionary<
string,
string>;
function Get(
const AValue:
string ):
string;
procedure Reset(
var AValue:
string );
public
constructor Create;
destructor Destroy;
override;
private
class var _Default: IStringCache;
class constructor Create;
public
class function Default: IStringCache;
end;
implementation
{ TStringCache }
constructor TStringCache.Create;
begin
FLock := TObject.Create;
inherited Create;
FDict := TDictionary<
string,
string>.Create;
end;
class constructor TStringCache.Create;
begin
TStringCache._Default := TStringCache.Create;
end;
class function TStringCache.
Default: IStringCache;
begin
Result := _Default;
end;
destructor TStringCache.Destroy;
begin
FreeAndNil( FDict );
FreeAndNil( FLock );
inherited;
end;
function TStringCache.Get(
const AValue:
string ):
string;
begin
TMonitor.Enter( FLock );
try
if not FDict.TryGetValue( AValue, Result )
then
begin
FDict.Add( AValue, AValue );
Exit( AValue );
end;
finally
TMonitor.Exit( FLock );
end;
end;
procedure TStringCache.Reset(
var AValue:
string );
begin
AValue := Get( AValue );
end;
end.
und das sind die Änderungen in deinem Beispiel-Projekt
Delphi-Quellcode:
uses
Common.StringCache;
{ TNameObject }
procedure TNameObject.set_Name( const Value: string );
begin
// fName := Value;
fName := TStringCache.Default.Get( Value );
end;
procedure TNameObject.set_Name_( Value: string );
begin
// fName := Value;
fName := TStringCache.Default.Get( Value );
end;
{ TBusinessObjectString }
procedure TBusinessObjectString.set_Name( const Value: string );
begin
// fName := Value;
fName := TStringCache.Default.Get( Value );
end;
procedure TBusinessObjectString.set_Name_( Value: string );
begin
// fName := Value;
fName := TStringCache.Default.Get( Value );
end;
und schwupps ist der Speicherverbrauch überall gleich.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ea 0a 4c 14 0d b6 3a a4 c1 c5 b9
dc 90 9d f0 e9 de 13 da 60)