Autor Beitrag
Stephan.Woebbeking
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: 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:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mi 03.08.11 13:08 
Moin!

user profile iconStephan.Woebbeking hat folgendes geschrieben Zum zitierten Posting springen:
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öhsenwith-Block implizit "aufheben":
ausblenden 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... ? :nixweiss:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Mi 03.08.11 13:10 
Naja eigentlich ist ja die Range am Recevier gekoppelt, dann hätte man es so aufbauen sollen:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 user profile iconDaniel 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 03.08.11 16:37 
user profile iconStephan.Woebbeking hat folgendes geschrieben Zum zitierten Posting springen:
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.
Genau das kannst du mit Interfaces ja machen. Denn der Code für die Freigabe durch Referenzzählung wird dann automatisch generiert.

user profile iconStephan.Woebbeking hat folgendes geschrieben Zum zitierten Posting springen:
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?
Aktualisiere mal deine Indyversion. ;-)
Stephan.Woebbeking Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: 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):

ausblenden volle Höhe 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:
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; // Das ist ja ganz unproblematisch
   val.value := val.value + 1// Wenn ich hier nicht andere "User" verwirren will, dann muss ich jedem seine eigene Instanz geben, richtig? Das ist der ganze Aufhänger...
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 04.08.11 14:48 
ausblenden volle Höhe 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:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 05.08.11 12:47 
Wenn man diese Funktion braucht, bleibt gar nichts anderes übrig. ;-)
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Fr 05.08.11 17:24 
Aus Bequemlichkeit wollen ja, aber etwas brauchen, was Delphi eigentlich gar nicht vorsieht :nixweiss: ? 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: So 07.08.11 03:01 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
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 user profile iconjaenicke 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 07.08.11 08:32 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Ich verwende Interfaces eigentlich nur für die Referenzzählung.
Ich benutze die durchaus auch für deren ursprünglichen Zweck. :mrgreen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 593
Erhaltene Danke: 5


Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
BeitragVerfasst: Sa 13.08.11 12:00 
Hi,

user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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.