Autor |
Beitrag |
ChZiegelt
Hält's aus hier
Beiträge: 6
|
Verfasst: Mo 06.07.09 12:34
Hallo zusammen,
ich habe ein kleines Problem mit Strings in D7.
Ich möchte gerne zwischen Threads Strings per Windows Message versenden.
Dazu habe ich bisher Objekte erzeugt, die den String speichern und dann die Adresse des Objektes versendet.
Dieser Weg klappt generell auch gut, prinzipiell möchte ich diesen Weg nicht ändern.
Nun gibt es aber Probleme mit dem String datentyp - ich möchte eine Datenstruktur verwenden, die ohne die optimierungen von Delphi funktioniert.
Also z.B. eine CharArray oder ein ByteArray.
Ich gebe einen String hinein, und er wird als Array gespeichert, ohne dass der original String dabei verändert wird, oder verloren geht.
Vor Allem bleibt mir das Problem, aus dem Array hinterher wieder EFFIZIENT einen String zu erstellen - als Kopie. Das Objekt soll weiter unangetatstet bleiben.
Ich möchte hierbei nicht mit einer Schleife mystring := myString + Arr[i]; nutzen.
Hat hier jemand Erfahrungen, ob ein ByteArray oder ein CharArray besser wäre, und wie erstelle ich einen normalen String aus dem Array.
Vielen Dank im Voraus.
Christian
Der gewünschte Weg
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| TCharObj = class(TMsgDataType) private FData: TCharArr;
function getData: TCharArr; procedure setData(const Value: TCharArr); function getStringData: String;
public constructor Create(const Data: TCharArr); constructor Create(const Data: String);
property Data: TCharArr read getData write setData; property StringData: String read getStringData;
function getSize: Integer; override; function Clone: TCharObj; end; |
Der bisherige Weg:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| TStrObj = class(TMsgDataType) private FData: PChar;
function getData: String; procedure setData(const Value: String); public property Data: String read getData write setData;
function getSize: Integer; override;
constructor Create(const Data: String); destructor Destroy; override;
function Clone: TMsgDataType; override;
end; |
Moderiert von Christian S.: Beiträge zusammengefasst
Eine kleine Erweiterung,
vielleicht habe ich das Problem aber auch schon quasi gelöst und sehe es nur nicht, hier einmal der komplette alte weg:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255:
| type
TMsgDataType = class(TInterfacedObject, ICloneableData) protected FTimeStamp: TDateTime; FTickStamp: DWord; procedure setTickStamp(const Value: DWord); procedure setTimestamp(const Value: TDateTime);
public constructor Create;
property TimeStamp: TDateTime read FTimeStamp write setTimestamp; property TickStamp: DWord read FTickStamp write setTickStamp;
function getSize: Integer; virtual; abstract; function Clone: TMsgDataType; virtual; abstract; end;
TStrObj = class(TMsgDataType) private FData: PChar;
function getData: String; procedure setData(const Value: String); public property Data: String read getData write setData;
function getSize: Integer; override;
constructor Create(const Data: String); destructor Destroy; override;
function Clone: TMsgDataType; override;
end;
implementation
function TStrObj.Clone: TMsgDataType; begin try try
Result := TStrObj.Create(Self.Data);
except On E: Exception do begin SiMain.EnterMethod(lvDebug, Self, 'Clone()', []); SiMain.LogException(E, E.Message); SiMain.LeaveMethod(lvDebug, Self, 'Clone()'); end; end; finally end;
end;
constructor TStrObj.Create(const Data: String); var len: Integer; begin inherited Create;
try try Self.Data := Data;
except On E: Exception do begin SiMain.EnterMethod(lvDebug, Self, 'Create()', []); SiMain.LogException(E, E.Message); SiMain.LeaveMethod(lvDebug, Self, 'Create()'); end; end; finally end;
end;
destructor TStrObj.Destroy; begin try try
if fData <> nil then StrDispose(FData);
inherited Destroy;
except On E: Exception do begin if (not E.InheritsFrom(EInvalidPointer)) and (not E.InheritsFrom(EAccessViolation)) then begin SiMain.EnterMethod(lvDebug, Self, 'Destroy()', []); SiMain.LogException(E, E.Message); SiMain.LeaveMethod(lvDebug, Self, 'Destroy()'); end; end; end; finally end;
end;
function TStrObj.getSize: Integer; begin try try Result := InstanceSize; except On E: Exception do begin SiMain.EnterMethod(lvDebug, Self, 'getSize()', []); SiMain.LogException(E, E.Message); SiMain.LeaveMethod(lvDebug, Self, 'getSize()'); end; end; finally end;
end;
function TStrObj.getData: String; begin try try
Result := FData;
except On E: Exception do begin SiMain.EnterMethod(lvDebug, Self, 'getData()', []); SiMain.LogException(E, E.Message); SiMain.LeaveMethod(lvDebug, Self, 'getData()'); end; end; finally
end;
end;
procedure TStrObj.setData(const Value: String); begin try try StrDispose(FData);
FData := StrNew( pChar(Value));
except On E: Exception do begin SiMain.EnterMethod(lvDebug, Self, 'setData()', []); SiMain.LogException(E, E.Message); SiMain.LeaveMethod(lvDebug, Self, 'setData()'); end; end; finally
end;
end; |
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 06.07.09 13:12
Moin und  im Forum!
ChZiegelt hat folgendes geschrieben : | Nun gibt es aber Probleme mit dem String datentyp - ich möchte eine Datenstruktur verwenden, die ohne die optimierungen von Delphi funktioniert. |
Warum? Grundsätzlich ist die Referenzzähler-Magic von Delphi eine sehr nützliche Sache (und ausserdem sehr schnell).
ChZiegelt hat folgendes geschrieben : | Vor Allem bleibt mir das Problem, aus dem Array hinterher wieder EFFIZIENT einen String zu erstellen - als Kopie. Das Objekt soll weiter unangetatstet bleiben.
Ich möchte hierbei nicht mit einer Schleife mystring := myString + Arr[i]; nutzen. |
Die Hampelei mit der Typkonversion würde ich mir sparen. Ruf einfach einmal UniqueString(MyString); auf, wenn du einen Referenzzähler von 1 erzwingen willst - den Rest macht auch hier die Compiler-Magic intern (und auch recht schnell).
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
ChZiegelt 
Hält's aus hier
Beiträge: 6
|
Verfasst: Mo 06.07.09 13:40
Hallo, danke für das herzliche Willkommen !
Also, UniqueString() scheint einen Teil des Problems zu lösen, Danke für den Tipp.
Jetzt gibt aber Unique String den selben String zurück, wenn der RefCounter auf 1 steht.
Vom System her habe ich vermutlich Strings nie komplett verstanden.
In Thread T1 werden Daten ampfangen und aus dem Puffer in eine Stringvariable gelegt S1. S1 ist ein Member der Klasse, damit sie nicht auf dem Stack der empfangenden Routine angelegt wird.
Nun soll der Inhalt von S1 an alle Obserever von T1 gesendet werden, und zwar jeweils als Kopie. Dabei soll S1 unverändert (Inhalt und Adresse) in T1 erhalten bleiben.
Welche Methode zum Dublizieren von S1 sollte man hier nehmen ?
Um das Stack Problem zu umgehen habe ich hier eine Member Variable der T1 Klasse benutzt, P1 als Paket vom Typ TStrObj.
Wenn ich einen standart String verwendet habe, ist beim Empfänger regelmäßig Murks angekommen.
Das war mein eigentliches Problem.
Danke schonmal
Christian
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 06.07.09 13:41
Und wenn man schnell mit einem String arbeiten will, dann kann man auch einfach diesen auf PChar casten und kann dann damit buchstabenweise arbeiten. Denn dies ist eigentlich das einzige, was an Strings langsam ist.
Wenn man natürlich dahergeht und immer ein Byte / einen Buchstaben zu einem Array / String hinzufügt, dann ist das sehr langsam. Das hat aber nichts mit Compilermagic zu tun, sondern einfach mit der eigenen äußerst ungünstigen Vorgehensweise...
Für das Versenden von Messages bietet sich PChar an. Es sollte eigentlich reichen den String darauf zu casten.
// EDIT:
Wenn der ursprüngliche String danach verändert werden soll, dann könntest du auch via GetMem Speicher für den PChar reservieren, die Daten hineinlegen, den verschicken und danach den Speicher wieder freigeben.
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 06.07.09 14:27
Moin!
ChZiegelt hat folgendes geschrieben : | Jetzt gibt aber Unique String den selben String zurück, wenn der RefCounter auf 1 steht. |
Und? FStringVar := UniqueString(FStringVar);, fertig, Referenzzähler ist auf 1.
ChZiegelt hat folgendes geschrieben : | In Thread T1 werden Daten ampfangen und aus dem Puffer in eine Stringvariable gelegt S1. S1 ist ein Member der Klasse, damit sie nicht auf dem Stack der empfangenden Routine angelegt wird. |
Dynamische Strings landen immer auf dem Heap (die Daten), lediglich der Zeiger darauf könnte auf dem Stack liegen.
ChZiegelt hat folgendes geschrieben : | Nun soll der Inhalt von S1 an alle Obserever von T1 gesendet werden, und zwar jeweils als Kopie. Dabei soll S1 unverändert (Inhalt und Adresse) in T1 erhalten bleiben.
Welche Methode zum Dublizieren von S1 sollte man hier nehmen ? |
Wenn du Objekte klonst, dann rufe einfach UniqueString dabei auf. Irgendwie verstehe ich nicht, wo du das Problem siehst.  Wobei man die Daten eigentlich nicht kopieren muss, wenn man diese versendet (was genau ist bei dir damit gemeint?). Das sollte nur nötig sein, wenn die Daten (zwischendurch) verändert werden.
ChZiegelt hat folgendes geschrieben : | Wenn ich einen standart String verwendet habe, ist beim Empfänger regelmäßig Murks angekommen. |
Das wird sicher nicht am "Standard-String" (was soll das eigentlich sein?) gelegen haben, sondern eher an nicht sauberer Threadsynchronisation (möglicherweise im Zusammenhang mit den referenzgezählten Strings - ich gebe zu, ich habe mir deinen Code nicht genauer angesehen  ).
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
ChZiegelt 
Hält's aus hier
Beiträge: 6
|
Verfasst: Mo 06.07.09 16:23
Danke nochmal für die Antworten.
Nun den Code reduziert auf das Wesentliche.
Zitat: | Und? FStringVar := UniqueString(FStringVar);, fertig, Referenzzähler ist auf 1. |
Was ich hieran nicht verstehe, ist was tatsächlich passiert. Die Funktion hat keinen Rückgabewert.
Wenn ich nun 2 Strings habe, S1, und S2 - S1 weise ich einen Wert zu, S2 setz ich gleich S1. Dann sollte doch die Referenzzählung erhöht werden, und beide auf die selbe Adresse verweisen.
In einem Test passiert dies aber nicht. Dieser Code liefert das Ergebnis darunter:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| var StackString: String; MyString: String;
begin StackString := edString.Text; memo.Lines.Add(Format('StackString: %p: %s',[ Addr(StackString), StackString])); memo.Lines.Add(Format('MyString: %p: %s',[ Addr(MyString), MyString])); MyString := StackString; memo.Lines.Add(Format('MyString: %p: %s',[ Addr(MyString), MyString])); UniqueString(StackString); memo.Lines.Add(Format('StackString: %p: %s',[ Addr(StackString), StackString])); |
Ergebniss:
StackString: 0012F424: edString
MyString: 0012F420:
MyString: 0012F420: edString
StackString: 0012F424: edString
Was die dynamischen Strings angeht, ich weiss nicht genau welche Du mit "dynamisch" meinst.
Folgender Code funktioniert nicht, sobald die procedure verlassen wird, sind die dort lokalen Strings nicht mehr gültig.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| procedure Klasse.BekommeDaten(Buffer: Pointer; Size: Cardinal); var Str: String; begin if Size > 0 then begin SetLength(Str, Size); CopyMemory(Pointer(Str), Buffer, Size); NotifyObserver( Str );
end;
end; |
Hier ist der Code wie ich ihn momentan verwende, ich weiss halt nur nicht ob das so funktionieren kann oder ob ich noch einem gedanklichen Kurzschluss unterliege:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:
|
function TStrObj.Clone: TMsgDataType; begin Result := TStrObj.Create(Self.Data);
end;
constructor TStrObj.Create(const Data: String); var len: Integer; begin inherited Create;
Self.Data := Data;
end;
destructor TStrObj.Destroy; begin if fData <> nil then StrDispose(FData);
inherited Destroy;
end;
function TStrObj.getData: String; begin Result := FData; end;
procedure TStrObj.setData(const Value: String); begin StrDispose(FData);
FData := StrNew( pChar(Value)); end; |
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 06.07.09 16:27
ChZiegelt hat folgendes geschrieben : | Was die dynamischen Strings angeht, ich weiss nicht genau welche Du mit "dynamisch" meinst.
Folgender Code funktioniert nicht, sobald die procedure verlassen wird, sind die dort lokalen Strings nicht mehr gültig. |
Das ist ja auch richtig so. Lokale Variablen sind eben lokale Variablen.
Reserviere eben mit GetMem den Speicher und benutze PChar, dann kannst du damit machen was du willst. Du darfst nur nicht vergessen den auch wieder freizugeben. PChars haben keine Referenzzählung und mit GetMem reservierter Speicher wird auch nicht automatisch wieder freigegeben.
|
|
ChZiegelt 
Hält's aus hier
Beiträge: 6
|
Verfasst: Mo 06.07.09 16:38
Gut, versuche ich es über diesen Weg,
Danke für die Antworten !!
Und noch einen schönen Abend
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mo 06.07.09 19:45
jaenicke hat folgendes geschrieben : | Und wenn man schnell mit einem String arbeiten will, dann kann man auch einfach diesen auf PChar casten und kann dann damit buchstabenweise arbeiten. Denn dies ist eigentlich das einzige, was an Strings langsam ist. |
Nein, da muss ich widersprechen. Manchmal ist PChar schneller, manchmal die Indizierung des Strings. Viel nehmen sie sich nicht, obwohl es wahrscheinlicher ist, mit PChar ein schnelleres Verfahren zu implementieren.
jaenicke hat folgendes geschrieben : | Wenn man natürlich dahergeht und immer ein Byte / einen Buchstaben zu einem Array / String hinzufügt, dann ist das sehr langsam. Das hat aber nichts mit Compilermagic zu tun, sondern einfach mit der eigenen äußerst ungünstigen Vorgehensweise...
|
Auch nein, denn erstens hat es etwas mit Compilermagic zu tun und zweitens werkelt mittlerweile der optimierte Memorymanager von FastMM in Delphi und der sorgt dafür, das fast kein Unterschied zwischen der zeichenweisen Konkatenation und dem einmaligen Reservieren und anschließende Befüllen besteht.
Hier eine sehr interessante Diskussion und ein Beispielprojekt: www.delphipraxis.net...ighlight=performance
Dessenungeachtet verstehe ich die Problematik hier nicht. Ebensowenig wie das Versenden eines Strings per Windows-Message. Wenn ich ein Observer-Pattern implementiere, kann ich den String doch einfach als 'Const' an die einzelnen Observer reichen. Wer eine Kopie anlegen möchte, kann das tun (per einfacher Zuweisung). Wer das nicht will, lässt es. Ist das nicht optimal, oder hab ich was verpasst?
_________________ Na denn, dann. Bis dann, denn.
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 06.07.09 21:53
Also das gehört zwar nicht direkt zum Thema, aber das muss ich dann doch noch dazu schreiben...
alzaimar hat folgendes geschrieben : | und zweitens werkelt mittlerweile der optimierte Memorymanager von FastMM in Delphi und der sorgt dafür, das fast kein Unterschied zwischen der zeichenweisen Konkatenation und dem einmaligen Reservieren und anschließende Befüllen besteht.
Hier eine sehr interessante Diskussion und ein Beispielprojekt: www.delphipraxis.net...ighlight=performance |
Da merkt man mal wieder den Unterschied zwischen irgendwelchen Beispielen und realen Projekten. Wenn ich das Auslesen aus einer .reg Datei (Unicode) bei meinem Registryeditor durch Stringkonkatenation mache, dauert es ca. 19,8 Sekunden für den gesamten Zweig HKLM. Wenn ich das selbe mit SetString + Pointern mache, dauert es ca. 9,1 Sekunden...
Und ich habe wirklich nur die paar Zeilen verändert.
Alt: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| if (FBufferPos = 0) and (FPosInBuffer = 0) then begin Inc(PByte(StartPointer), 2); SetString(AValue, nil, LineLen div 2 - 1); end else SetString(AValue, nil, LineLen div 2); if Length(AValue) > 0 then begin CurValue := PByte(AValue); while Integer(StartPointer) < Integer(CurData) do begin CurValue^ := PByte(StartPointer)^; Inc(PByte(StartPointer), 2); Inc(CurValue); end; end; | "Neu": Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| if (FBufferPos = 0) and (FPosInBuffer = 0) then Inc(PByte(StartPointer), 2); AValue := ''; while Integer(StartPointer) < Integer(CurData) do begin AValue := AValue + PChar(StartPointer)^; Inc(PByte(StartPointer), 2); end; | Du kannst es gerne selbst in der Unit FileAccessUtils.pas des Projekts ausprobieren...
|
|
ChZiegelt 
Hält's aus hier
Beiträge: 6
|
Verfasst: Mo 06.07.09 23:53
Ok, ich hatte zwar nicht mit weiteren Meldungen gerechnet, aber trotzdem danke
Zitat: |
Dessenungeachtet verstehe ich die Problematik hier nicht. Ebensowenig wie das Versenden eines Strings per Windows-Message. Wenn ich ein Observer-Pattern implementiere, kann ich den String doch einfach als 'Const' an die einzelnen Observer reichen. Wer eine Kopie anlegen möchte, kann das tun (per einfacher Zuweisung). Wer das nicht will, lässt es. Ist das nicht optimal, oder hab ich was verpasst? |
Die Problematik war halt, dass es für mich nicht ersichtlich war, wann ein String tatsächlich dubliziert wird, und wann er mit neuer Referenz versehen wird.
Ursprünglich senden 2 bis 5 Komunikations Threads empfangene Daten an Ihre Observer. Diese sammeln die Strings so lange bis ein gültiges Telegramm vorliegt, und bearbeiten es weiter.
Ich nutze keine Methode mit const Übergabe, um keine Locks der Threads zu erzeugen - kann mir also die Synchronisierung sparen.
Um die Gefahr zu vermeiden, dass die PostMessage() nicht empfangen wird, oder verloren geht, werden alle versendeten Strings vom Versender in eine Liste eingetragen (deswegen das Objekt um den String herum, aber auch aus anderen Gründen wie Timestamp, Clone,…), um später ab einer bestimmten Zeit befreit zu werden.
Nun ergab es sich das eine oder andere MAl dass Zugriffsverletzungen auftraten, weil Strings nicht mehr zugewiesen waren (die ContainerObjekte aber durchaus).
Ich wollte vor Allem von jemadem der sich damit auskennt wissen, ob der Code soweit er dort steht funktioniert, oder ob ich auf Alternativen ausweichen sollte.
Da ich Strings im Detail nicht verstanden zu haben scheine, dachte ich könnte ich das Problem mit einem Char Array oder ähnlichem lösen, da wüsste ich wann ich wo speicher reserviere und wann ich ihn wieder freigeben muss.
Hierzu war meine Frage ob das effizient lösbar ist.
effizient heisst bei mir, jeder Com Thread bekommt etwa alle 20ms bis 250ms einen String rein, schickt den weiter an beliebig viele Observer und ist wieder empfangsbereit.
Gruß
Christian
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Di 07.07.09 00:08
Moin!
So, jetzt nochmal in Ruhe drübergeschaut (war heute tagsüber leider nicht drin).
ChZiegelt hat folgendes geschrieben : | Die Funktion hat keinen Rückgabewert. |
Ja, das ist richtig  ich hatte keine Zeit, in die Doku zu schauen.
ChZiegelt hat folgendes geschrieben : | Was ich hieran nicht verstehe, ist was tatsächlich passiert. |
Das liegt vermutlich daran, dass dein Test-Beispiel nicht korrekt ist: Addr() liefert die Adresse der Variablen, nicht die Adresse, auf die eine Zeigervariable zeigt (=Inhalt). Das hier wäre korrekt und ist auch gleich um den Referenzzähler erweitert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| var Form1: TForm1; MyString: String;
procedure TForm1.Button1Click(Sender: TObject); var StackString: String;
function GetRefCount(const AStr: String): Cardinal; begin if (AStr <> '') then Result := PCardinal(Integer(Pointer(AStr))-8)^ else Result := 0; end;
begin StackString := edString.Text; Memo.Lines.Add(Format('StackString: %p[%d]: %s',[ Pointer(StackString), GetRefCount(StackString), StackString])); Memo.Lines.Add(Format('MyString: %p[%d]: %s',[ Pointer(MyString), GetRefCount(MyString), MyString])); MyString := StackString; Memo.Lines.Add('MyString := StackString;'); Memo.Lines.Add(Format('StackString: %p[%d]: %s',[ Pointer(StackString), GetRefCount(StackString), StackString])); Memo.Lines.Add(Format('MyString: %p[%d]: %s',[ Pointer(MyString), GetRefCount(MyString), MyString])); UniqueString(StackString); Memo.Lines.Add('UniqueString(StackString);'); Memo.Lines.Add(Format('StackString: %p[%d]: %s',[ Pointer(StackString), GetRefCount(StackString), StackString])); Memo.Lines.Add(Format('MyString: %p[%d]: %s',[ Pointer(MyString), GetRefCount(MyString), MyString])); end; | Liefert folgende Ausgabe:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| StackString: 00982550[1]: edString MyString: 00000000[0]: MyString := StackString; StackString: 00982550[2]: edString MyString: 00982550[2]: edString UniqueString(StackString); StackString: 009843C4[1]: edString MyString: 00982550[1]: edString |
ChZiegelt hat folgendes geschrieben : | Was die dynamischen Strings angeht, ich weiss nicht genau welche Du mit "dynamisch" meinst. |
Strings, die mit "beliebiger" Länge auf dem Heap verwaltet werden.
ChZiegelt hat folgendes geschrieben : | Folgender Code funktioniert nicht, sobald die procedure verlassen wird, sind die dort lokalen Strings nicht mehr gültig. |
Das ist klar, hat jaenicke ja auch schon erläutert.
Gib einfach die Referenz auf den String weiter (durch eine einfache Zuweisung), dann kannst du auch sogar die lokale Variable für den Empfang beibehalten:
ChZiegelt hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| procedure Klasse.BekommeDaten(Buffer: Pointer; Size: Cardinal); var Str: String; begin if Size > 0 then begin SetLength(Str, Size); CopyMemory(Pointer(Str), Buffer, Size); NotifyObserver( Str ); end; end; | |
Das sollte im Wesentlichen alzaimars Vorschlag entsprechen.
jaenicke hat folgendes geschrieben : | Reserviere eben mit GetMem den Speicher und benutze PChar, dann kannst du damit machen was du willst. Du darfst nur nicht vergessen den auch wieder freizugeben. PChars haben keine Referenzzählung und mit GetMem reservierter Speicher wird auch nicht automatisch wieder freigegeben. |
Das ist in einer multithreaded Umgebung kein wirklich guter Ansatz, da hier die Referenzzählung an dir kleben bleibt - das macht aber Delphi doch gerne für dich.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Di 07.07.09 07:44
_________________ Na denn, dann. Bis dann, denn.
|
|
ChZiegelt 
Hält's aus hier
Beiträge: 6
|
Verfasst: Di 07.07.09 16:30
Na Ihr beide mögt Euch ja
Wegen mir braucht Ihr Euch aber nicht zu zerfleichen…
Noch einmal zurück zu dem Thema:
So langsam denke ich habe ichs mit Strings, allerdings komme ich um das Container Objekt nicht herum.
Sehe ich das richtig, dass ich simpel ein Container Objekt (StrObj) anlege und den Str aus der Datenempfangsroutine dem Objekt zuweise ?
Delphi-Quelltext
Und im Zweifel noch die UniqueString() Routine darauf ausführe ?
Gruß
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Di 07.07.09 16:48
Moin!
ChZiegelt hat folgendes geschrieben : | Sehe ich das richtig, dass ich simpel ein Container Objekt (StrObj) anlege und den Str aus der Datenempfangsroutine dem Objekt zuweise ?
Delphi-Quelltext
Und im Zweifel noch die UniqueString() Routine darauf ausführe ? |
Ja, würde ich so sehen. Lass das UniqueString() erstmal weg, solange du in den Threads nur die Strings liest, sollte das wunderbar klappen.  Du brauchst dich auch nicht um die Freigabe der Strings kümmern, gib einfach den Container frei, die CompilerMagic sollte den String/Referenz sauber wegräumen.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
|