Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Erzeugen und Freigeben von Objekten und Speicher (https://www.delphipraxis.net/93313-erzeugen-und-freigeben-von-objekten-und-speicher.html)

guidobrose 4. Jun 2007 10:35


Erzeugen und Freigeben von Objekten und Speicher
 
Ich hätte einmal eine grundsätzliche Frage zu Objekten.

Ich möchte die Objects Eigenschaft des StringGrid verwenden, um zusätzliche Formatierungsoptionen für die einzelne Zelle zu speichern. Ggf. soll über die neue Eigenschaft IsBitmap auch eine Grafik angezeigt werden können.

Hier einmal eine beispielhafte Deklaration des Objektes für die Eingenschaften:
Delphi-Quellcode:
type

  TCellOption = class(TObject)
  public
    Font: TFont;
    IsBitmap: Boolean;
    Bitmap: TBitmap;
  end;

  ...
Hier wäre jetzt die Zuweisung an jede Zelle des StringGrid:

Delphi-Quellcode:
  ACellOption:=TCellOption.Create;
  StringGrid.Objects[..]:=ACellOption;
Jetzt kann ich allerdings noch nichts an Font zuweisen, weil ja noch keine Instanz davon erzeugt wurde, also könnte es so aussehen:

Delphi-Quellcode:
  ACellOption:=TCellOption.Create;
  ACellOption.Font:=TFont.Create;
  ACellOption.Font.Assign(Font);
  StringGrid.Objects[..]:=ACellOption;
Genauso wird dann auch mit Bitmap verfahren nur zunächst ohne ein Bitmap zuzuweisen, da es ja nicht in jeder Zelle unbedingt benötigt wird.

Erste Frage:

Wenn ich die so erzeugten Objekte wieder freigeben möchte, welche Methoden muss ich dann aufrufen?

StringGrid.Objects[..].Free?

Damit ist die Instanz von TFont aber noch nicht freigegeben, oder?

Sollte ich also geschickterweise die create und destroy-Methoden von TCellOption überschreiben und darin die Instanz von TFont (und TBitmap) erzeugen und freigeben?

Zweite Frage:

Wie verhält es sich mit dem Speicherbedarf, wenn ich direkt eine Instanz von TBitmap pro Zelle erzeuge, ohne das ich sie evtl. wirklich benötige?

Danke und Gruß,
Guido

marabu 4. Jun 2007 10:46

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Hallo Guido,

du brauchst einen eigenen Destruktor, wenn das Objekt Eigentümer z.B. des Font sein soll. Anders wäre es, wenn nur eine Referenz gespeichert würde.

Was die Bitmap angeht: Probiere es einfach aus und messe den Verbrauch, dann weißt du wie skalierfähig dein Ansatz ist. Oft verwaltet man Bilder in einer ImageList und setzt beim Objekt nur eine Referenz, aber das musst du erkennen.

Grüße vom marabu

shmia 4. Jun 2007 10:50

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Zitat:

Zitat von guidobrose
Erste Frage:
Wenn ich die so erzeugten Objekte wieder freigeben möchte, welche Methoden muss ich dann aufrufen?
StringGrid.Objects[..].Free?

Ja!
Zitat:

Zitat von guidobrose
Damit ist die Instanz von TFont aber noch nicht freigegeben, oder?

Dafür ist die Klasse TCellOption verantwortlich.
Zitat:

Zitat von guidobrose
Sollte ich also geschickterweise die create und destroy-Methoden von TCellOption überschreiben und darin die Instanz von TFont (und TBitmap) erzeugen und freigeben?

Definitiv!!

Zitat:

Zitat von guidobrose
Zweite Frage:
Wie verhält es sich mit dem Speicherbedarf, wenn ich direkt eine Instanz von TBitmap pro Zelle erzeuge, ohne das ich sie evtl. wirklich benötige?

Der benötigte Platz eines leere Bitmap-Objekts lässt sich über die Funktion InstanceSize abfragen.
Eingebettete Objekte werden dabei nicht berücksichtigt (bzw. nur 4 Bytes für den Objektzeiger).
Ein gefülltes Bitmap benötigt dann noch zusätzliche Resourcen.

Der_Unwissende 4. Jun 2007 10:59

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Zitat:

Zitat von guidobrose
Jetzt kann ich allerdings noch nichts an Font zuweisen, weil ja noch keine Instanz davon erzeugt wurde, also könnte es so aussehen:

Delphi-Quellcode:
  ACellOption:=TCellOption.Create;
  ACellOption.Font:=TFont.Create;
  ACellOption.Font.Assign(Font);
  StringGrid.Objects[..]:=ACellOption;
Genauso wird dann auch mit Bitmap verfahren nur zunächst ohne ein Bitmap zuzuweisen, da es ja nicht in jeder Zelle unbedingt benötigt wird.

Hi,
hier gibt es schon die erste Stelle an der Du aufpassen solltest. Wenn Du mit Create eine neue Instanz erzeugst, dann wird Dir eine Referenz auf dieses neue Objekt zurück gegeben. Genau diese Referenz wird in ACellOption.Font gespeichert. Weißt Du hier einer TCellOption zweimal einen Font zu, so überschreibst Du nur die alte Referenz. Das Objekt, dass Du angelegt hast würde einfach im Speicher bleiben (und wäre nicht mehr erreichbar). Besser ist es hier, dass Du vorher ein Free aufrufst um sicherzustellen, dass es keine erste Instanz gibst, die sonst ein Speicherleck darstellt.

Zitat:

Zitat von guidobrose
Erste Frage:

Wenn ich die so erzeugten Objekte wieder freigeben möchte, welche Methoden muss ich dann aufrufen?

StringGrid.Objects[..].Free?

Damit ist die Instanz von TFont aber noch nicht freigegeben, oder?

Korrekt!

Zitat:

Zitat von guidobrose
Sollte ich also geschickterweise die create und destroy-Methoden von TCellOption überschreiben und darin die Instanz von TFont (und TBitmap) erzeugen und freigeben?

Ja!

Zitat:

Zitat von guidobrose
Zweite Frage:

Wie verhält es sich mit dem Speicherbedarf, wenn ich direkt eine Instanz von TBitmap pro Zelle erzeuge, ohne das ich sie evtl. wirklich benötige?

Du hast einen recht geringen Speicherbedarf. Das was ein Bitmap-Objekt wirklich groß macht ist die eigentliche Bitmap (das Bild). Ein TBitmap-Objekt besteht aus ein wenig mehr, so hast Du halt die Meta-Informationen einer Bitmap (Höhe, Breite, Farbtiefe, ...) und ein wenig VCL-Overhead (Handle, Pen, Brush, Canvas,...) aber an sich sollten dass nur ein paar zig oder hundert Byte sein. Das eigentlich große kommt erst hinzu, wenn Du hier eine TBitmap zuweist.

An sich wäre eventuell eine gute Möglichkeit für Dich, dass Du die Zuweisung über Properties vornimmst. Die erlauben es Dir, dass Du eben Speicher nur bei Bedarf zuweist.
Grob könnte es die folgende Form haben:
Delphi-Quellcode:
TCellOption = class(TObject)
  private
    FFont: TFont;
    Bitmap: TBitmap;
  protected
    procedure setBitmap(const Bitmap: TBitmap);
    procedure setFont(const Font: TFont);
  public
    destructor Destroy; override;
    function hasBitmap(): Boolean;
   
    property Bitmap: TBitmap read FBitmap write setBitmap;
    property Font: TFont read FFont write setFont;
  end;
Die Implementierung könnte dann so aussehen:
Delphi-Quellcode:
destructor TCellOption.Destroy;
begin
  self.FBitmap.Free;
  self.FFont.Free;
 
  inherited Destroy;
end;

function TCellOption.hasBitmap: Boolean;
begin
  result := assgigned(self.FBitmap);
end;

procedure TCellOption.setBitmap(const Bitmap: TBitmap);
begin
  if assigned(Bitmap) then
  begin
    // wenn self.FBitmap nichts zugewiesen ist
    // wird einen neue Instanz erzeugt
    if not assigned(self.FBitmap) then
    begin
      self.FBitmap := TBitmap.Create;
    end;

    self.FBitmap.Assign(Bitmap);
  end

  // wenn dem TBitmap-Objekt nil zugewiesen wird, dann wird die Bitmap einfach gelöscht
  else
  begin
    self.FBitmap.Free;
    self.FBitmap := nil;
  end;
end;

// analog für setFont
Gruß Der Unwissende

[roter Kasten]
Ok, das meiste hier wurde schon gesagt, aber ich poste es mal trotzdem!
[/roter Kasten]

guidobrose 4. Jun 2007 11:17

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Danke für die Antworten! Ich bin ja anscheinend auf dem richtigen Weg und werde die hier erhaltenen Informationen mal in ein paar Versuchen verwerten.

Kurze Nachfrage noch:

Was bewirken denn self.FBitmap oder self.FFont.Free ?

Wo liegt der Unterschied zu FFont.Free?

Danke.

Der_Unwissende 4. Jun 2007 11:20

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Zitat:

Zitat von guidobrose
Kurze Nachfrage noch:

Was bewirken denn self.FBitmap oder self.FFont.Free ?

Wo liegt der Unterschied zu FFont.Free?

Es gibt keinen direkten Unterschied! Mit self qualifiziert man nur Instanzvariablen. Das heißt, dass self zeigt nur deutlicher an, dass es sich eben um Variablen handelt, die zur gleichen Instanz gehören, zu denen auch der Methodenaufruf gehört. Hättest Du zum Beispiel Instanzvariablen, die den gleichen Namen haben, wie eine lokale Variable, kannst Du die so unterscheiden

Jelly 4. Jun 2007 11:53

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Ich würde bereits im constructor von TCellOption eine Instanz von Font und Bitmap erstellen, und diese im Destructor natürlich auch wieder freigeben.

So kann von aussen, nach Erstellen einer TCellOption Instanz direkt auf dessen Font und Bitmap zugegriffen werden, ohne eine Zugriffsverletzung zu bekommen.

guidobrose 5. Jun 2007 12:22

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Da es gut zum Thema passt, noch eine Frage hinterher:

Wenn ich eine Instanz mit Create(AOwner) erzeuge muss ich dies dann wieder explizit Freigeben oder erlegigt das der Owner automatisch, wenn er freigegeben wird?

z.B. Erstelle ich auf einem Formular eine x-beliebige Komponente:

Delphi-Quellcode:
procedure TForm1.Create;
begin
  Panel:=TPanel.Create(self);
  Panel.Parent:=Form1;
end;
Ist dann in TForm1.Destroy noch notwendig Panel.Free aufzurufen?

hoika 5. Jun 2007 12:32

Re: Erzeugen und Freigeben von Objekten und Speicher
 
Hallo,

durch das .(Self) wird das Form der Owner
und gibt das Panel auch frei.

Ich empfehle memcheck, um Speicherfehler zu finden.


Heiko


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:18 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz