Autor |
Beitrag |
Stephan.Woebbeking
      
Beiträge: 97
|
Verfasst: Mi 03.08.11 11:56
Hallo *.
Nachdem ich mir da bisher keine Gedanken drum gemacht hab (und auch keine Probleme hatte), habe ich jetzt angefangen meine Programme mal etwas auf Speicherlecks zu untersuchen. Als langjähriger Java Befürworter- und Programmierer könnt ihr Euch ausdenken wie das aussieht - der Carbage Collector ist spätestens jetzt mein Freund.
Konkrete Frage: Konstrukte mit anonymen Objekten wie diese finden sich in Java zu Hauf und auch in Delphi macht es gelegentlich viel Sinn:
Delphi-Quelltext 1:
| if ( receiver.GetReceiverRange.IsInRange( TransmitterList.CurrentTransFreq ) ) then |
Die Methode "GetReceiverRange" erzeugt ein Objekt. Da ich es nicht zuweise kann ich auch kein "Free" aufrufen, bekomme also ein Speicherleck. Wie würde man vorgehen, die einzige Möglichkeit die ich so sehe ist, die Referenz aufzuheben und dann eben ein "Free" zu machen. Gibt's noch was anderes?
Danke,
Stephan
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mi 03.08.11 13:08
Moin!
Stephan.Woebbeking hat folgendes geschrieben : | Wie würde man vorgehen, die einzige Möglichkeit die ich so sehe ist, die Referenz aufzuheben und dann eben ein "Free" zu machen. Gibt's noch was anderes? |
Ganz ohne "Aufheben" geht in Delphi nicht, das ist klar.  Du kannst aber mit einem böhsen™ with-Block implizit "aufheben":
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| with receiver.GetReceiverRange do try if IsInRange( TransmitterList.CurrentTransFreq ) then finally Free; end; | Ob das jetzt besser ist... ?
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Mi 03.08.11 13:10
Naja eigentlich ist ja die Range am Recevier gekoppelt, dann hätte man es so aufbauen sollen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| type TReceiver = class(Tobject) private FReceiverRange: TReceiverRange; public property Range: TReceiverRange read FReceiverRange; end; |
und im TReceiver die Range erstellen/freigeben. So wäre es sauber gelöst. Ansonsten helfen dir eventl. Interfaces.
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
Lemmy
      
Beiträge: 792
Erhaltene Danke: 49
Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
|
Verfasst: Mi 03.08.11 13:56
Hi,
ich sehe da noch kein zwingendes Speicherleck, denn hier wäre dann "receiver" für die Freigabe verantwortlich - schließlich wird die Instanz auch von Receiver erzeugt. Und da hättest Du dann div. Möglichkeiten.
Sollte GetReveiverRange eine Anforderung eines Singletons sein, dann hättest Du immer noch im Finalization Bereich der unit die Möglichkeit die Instanz wieder freizugeben
Grüße
|
|
Stephan.Woebbeking 
      
Beiträge: 97
|
Verfasst: Mi 03.08.11 14:15
Ok, an der Stelle fällt mir auf, dass ich euch eine Information vorenthalten habe (nicht absichtlich): Das Rückgabeobjekt wird in der Prozedur erzeugt, damit der Empfänger mit irgendwelchen Berechnungen auf dem Rückgabeobjekt (das kommt häufiger vor) keine Seiteneffekte im Receiver erzeugt. Darauf aufbauend wäre es vielleicht richtig, dem Receiver bereits ein Objekt zu geben, das könnte ich dann in der rufenden Methode auch wieder freigeben. Aber der (Schreib-) Aufwand ist deutlich höher; natürlich habe ich dann kein anonymes Objekt mehr, d.h. ich kann es auch freigeben. Aber lohnt sich der zusätzliche Aufwand dann wirklich?
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 03.08.11 14:44
Ein Objekt zu erzeugen und in einer Funktion zurückzugeben ohne sich weiter darum zu kümmern, ist einfach nur unsauberer Code durch ein schlechtes Konzept.
Aber wenn du das so unbedingt machen möchtest, kannst du ja wie Daniel schon geschrieben hat, Interfaces verwenden. Das Objekt wird dann automatisch freigegeben, wenn alle Referenzen darauf aus dem Scope sind oder explizit auf nil gesetzt wurden.
|
|
Stephan.Woebbeking 
      
Beiträge: 97
|
Verfasst: Mi 03.08.11 15:40
Hm, also direkt so kann ich das nicht umsetzen. Denn damit verteile ich ja jedesmal DASSELBE Objekt, was ich eigentlich tue ist, jedesmal eine KOPIE anzufertigen, damit jeder seine eigene Instanz bekommt.
Noch eine andere Frage:
Nachdem ich erstmal etliches geradegezogen habe, bekomme ich noch einen TIdThreadSafeInteger und zwei TIdCriticalSection gemeldet. Ich bin mir aber keiner Schuld bewusst?
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 03.08.11 16:37
|
|
Stephan.Woebbeking 
      
Beiträge: 97
|
Verfasst: Do 04.08.11 12:41
Wow, Interfaces sind 'ne klasse Sache - in Java, in Delphi war mein Stand immer noch, dass es das dort nicht gibt... Man lernt doch immer noch dazu (nicht jeder, aber lass uns das nicht vertiefen...  ).
Ok, jetzt habe ich die Funktion, die mein Objekt erzeugt und dann zurückgibt in ein Interface gepackt (von IInterface abgeleitet) und meine eigentliche Klasse von TInterfacedObject abgleitet. Dann habe ich noch das .Free; in der rufenden Funktion entfernt. Die übergebene Instanz wird aber jetzt wieder als Speicherleck identifiziert. Muss ich sonst noch was ein/umstellen?
Stephan
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 04.08.11 13:36
Die Klasse, um die es geht, musst du von TInterfacedObject ableiten und ein Interface zusätzlich erstellen, das deine Klasse implementiert. Zurückliefern tust du dann nur das Interface.
Die Klasse, in der du die Funktion liegt, die das zurückgibt, braucht kein Interface.
|
|
Stephan.Woebbeking 
      
Beiträge: 97
|
Verfasst: Do 04.08.11 14:05
Ich muss gestehen, das habe ich jetzt nicht wirklich verstanden... Lass mich mal kurz die Ist-Situation skizzieren (kein vollständiges Program, nur prinzipiell):
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:
| interface
class ValueWrapper value: type; end;
class Provider function GetValue: ValueWrapper; end;
class User function DoSomething; end;
var providerInstance: Provider;
implementation
Provider.GetValue; begin Result := ValueWrapper.Create(); Result.value := ...; end;
User.DoSomething; var val: ValueWrapper; begin val := providerInstance.GetValue; ... := val.value; val.value := val.value + 1; end; |
Wenn ich jetzt nur ein Interface zurückliefern würde, habe ich doch keinen Wert den ich verwenden und auch ändern kann?
Stephan
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 04.08.11 14:48
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:
| type ITrash = interface function GetTest: string; procedure SetTest(const Value: string); property Test: string read GetTest write setTest; end;
TTrash = class(TInterfacedObject, IInterface) private FTest: string; function GetTest: string; procedure SetTest(const Value: string); public property Test: string read GetTest write SetTest; end;
TProvider = class public function GetTrash: ITrash; end;
function TTrash.GetTest: string; begin Result := FTest; end;
procedure TTrash.SetTest(const Value: string); begin FTest := Value; end;
function TProvider.GetTrash: ITrash; begin Result := TTrash.Create; Result.Test := 'abc'; end; | Ungetestet, sollte so klappen.
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Fr 05.08.11 12:13
Kurzer Einwurf von einem mittlerweile eher außerhalb von Delphi stehenden: Es ist doch aber immer noch nicht die Delphi-Norm, in der Art Interfaces nur um des Freigebns willen einzuführen, oder? Ich denke nicht, dass man so um die Sprache herumprogrammieren sollte.
_________________ >λ=
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 05.08.11 12:47
Wenn man diese Funktion braucht, bleibt gar nichts anderes übrig. 
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Fr 05.08.11 17:24
Aus Bequemlichkeit wollen ja, aber etwas brauchen, was Delphi eigentlich gar nicht vorsieht  ? In Delphi ist es nunmal so gedacht, dass man sich um das Speichermanagement von Klassen selbst kümmern muss, da ist doch ein "hmja, eigentlich haben wir Interfaces nur wegen COM eingeführt"-Artefakt kein Ausweg.
_________________ >λ=
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 05.08.11 19:08
Ich mache es auch eher umgekehrt, ich schalte durch entsprechende Implementierung der Referenzzählungsmethoden selbige aus. Damit ich mich selbst darum kümmern kann.
Sauberer wäre es schon mit einem anderen Konzept. Trotzdem funktioniert es so auch gut.
Eine andere Möglichkeit wären eventuell auch Records als Rückgabewerte. Dann übernimmt Delphi auch die Speicherverwaltung. Da Records auch Konstruktoren und Methoden beinhalten können, sind diese in einem Fall wie diesem oft sehr sinnvoll.
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: So 07.08.11 03:01
jaenicke hat folgendes geschrieben : | Eine andere Möglichkeit wären eventuell auch Records als Rückgabewerte. Dann übernimmt Delphi auch die Speicherverwaltung. Da Records auch Konstruktoren und Methoden beinhalten können, sind diese in einem Fall wie diesem oft sehr sinnvoll. |
Und für ältere Delphis kann man auch object verwenden. Verhält sich wie Records in XE, werden allerdings auf dem Stack alloziiert. Also nicht zu groß machen
Hatte ich so verwendet im Bot für die eeBotLiga, und es war ordentlich schnell.
Ich verwende Interfaces eigentlich nur für die Referenzzählung. Da gabs mal irgendwann einen Thread zu, in dem jaenicke z.B. Container-Objekte vorgeschlagen hat. Dann muss man sich nicht weiter damit beschäftigen, dass Properties sehr unpraktisch werden, sondern kann einfach die Objekte verwenden wie gedacht.
Sind aber alles Krücken, ja. Ohne im Compiler rumzuhacken wird das nicht sauber was.
Einigermaßen ordentlich implementiert hat Borland das im Notification-System von TComponent. Das basiert aber eben darauf, dass man an jeder stelle selbst die entsprechenden Notifications versenden muss. Hat natürlich nichts mit dem eigentlichen Ziel "schönes Chaining" zu tun.
[persönlicherEindruck]Um schön zu programmieren ist Delphi einfach nicht die richtige Sprache; es ist eher produktiv aber dafür oft hässlich.[/persönlicherEindruck]
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 07.08.11 08:32
Martok hat folgendes geschrieben : | Ich verwende Interfaces eigentlich nur für die Referenzzählung. |
Ich benutze die durchaus auch für deren ursprünglichen Zweck.
Die sind auch für reine Delphiprogramme dabei nützlich, denn auf diese Weise kannst du z.B. ein Objekt als von IUnknown abgeleitetes Interface an eine DLL übergeben. (Und hast als Bonus die Möglichkeit die DLL auch in anderen Sprachen zu nutzen.)
object würde ich aber nicht mehr verwenden, das gibt es nur für Kompatibilität mit Turbo Pascal noch.  Seit Delphi 2006 hat man ja auch in Records ähnliche Funktionalität und noch mehr.
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Sa 13.08.11 12:00
Hi,
Martok hat folgendes geschrieben : |
Und für ältere Delphis kann man auch object verwenden.
|
Wobei "aeltere Delphis" bedeutet: Delphi 1 bis XE (zu XE2 kann ich noch nichts sagen, aber es wuerde mich nicht wundern, wenn selbst die 64 Bit Version noch compatibel dazu waere).
twm
|
|
jaenicke
      
Beiträge: 19326
Erhaltene Danke: 1749
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 13.08.11 12:37
Dass es das noch gibt, heißt aber nicht, dass man es verwenden sollte. Das ist wie mit WinExec oder TThread.Resume, das gibt es auch noch, sollte aber ebenfalls nicht mehr verwendet werden.
|
|