Thema: Delphi Problem TStringlist.free

Einzelnen Beitrag anzeigen

xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#7

Re: Problem TStringlist.free

  Alt 26. Dez 2005, 17:12
Hallo,

ich kenne leider kein gutes Tutorial für Interfaces. Aber ich versuche mal eine kleine Erklärung:

Interfaces sind im Grunde abstrakte Klassen. Sie beschreiben einen definierten Funktionsumfang einer Klasse. Hier mal ein Beispiel:
Delphi-Quellcode:
type
  IMyInterface = interface(IInterface)
  ['{7D30C89B-C144-420B-8F2C-501FEE4D787B}']
    function GetName: String;
    procedure SetName(const Value: String);
    procedure PrintName;
    property Name: String read GetName write SetName;
Wichtig ist: Ein Interface definiert keine Variablen. Folglich müssen alle Variablen über Properties mittels Getter und Setter definiert werden (siehe Name). Zusätzlich sollte ein Interface eine GUID haben, über die es eindeutig erkannt werden kann (['{...}']).

So wird ein Interface implementiert:
Delphi-Quellcode:
type
  TMyClass = class(TInterfacedObject, IMyInterface)
  private
    FName: String;
    function GetName: String;
    procedure SetName(const Value: String);
  public
    procedure MachWasAnderes;
    procedure PrintName;
    property Name: String read GetName write SetName;
  end;
Wichtig:
Alle Methoden, die im Interface definiert sind, müssen auch implementiert werden. Zusätzlich können noch beliebige weitere Methoden definiert werden.
Eine solche Klasse muss immer von TInterfacedObject abgeleitet werden, da dort einige Methoden definiert werden (z. B. Referenzählung, siehe unten).

Zur Benutzung:
Ein Interface besitzt einn Referenzzähler. Wenn dieser Null erreicht, zerstört sich das Objekt automatisch. Bei jeder Zuweisung einer Variablen wird der Referenzzähler automatisch erhöht. So weiß das Objekt, ob es noch benötigt wird. Dies hat einige Auswirkungen:
Interfaces sollten immer mit const übergeben werden:procedure TuWas(const Intf: IInterface); Interfaces sollten immer am Ende freigegeben werden, indem die Variable auf nil gesetzt wird:
Delphi-Quellcode:
procedure MachWas;
var
  Intf: IInterface;
begin
  Intf := CreateIrgendeinInterface;
  Intf.Blubber;
  intf := nil; <- Hier wird Intf freigegeben, da keine weitere Variable darauf zeigt
end;
Wenn ein Interface freigegeben wird, wird automatisch der Destructor des Objekts aufgerufen.

Da Interfaces sich selbst freigeben, sollte man immer darauf achten, am Ende des Programms sämtliche Referenzen zu entfernen, sonst erzeugt man ein Speicherleck.
Wenn eine Klasse, die ein Interface implementiert, einer Klassenreferenz zugewiesen wird, so wird der Referenzzähler nicht erhöht.

Nochmal mit obigem Beispiel zur Verdeutlichung:
Delphi-Quellcode:
procedure Eins;
var
  Obj: TMyClass;
  Intf: IMyInterface;
begin
  Obj := TMyClass.Create; // Referenzzähler = 0
  Obj.Name := 'Meier';
  Intf := Obj; // Referenzzähler = 1
  Intf.PrintName;
  Obj.MachWasAnderes;
  Intf := nil; // Referenzzähler = 0 -> Objekt wird zerstört
  Obj.Free; // Zugriffsfehler, Objekt ist schon zerstört
end;

procedure Zwei;
var
  Intf: IMyInterface;
begin
  Intf := TMyClass.Create; // Referenzzähler = 1;
  Intf.Name := 'Meier';
  Intf.PrintName;
  Intf.MachWasAnderes; <- Das geht nicht, MachWasAnderes ist in der Klasse definiert, nicht im interface
  Intf := nil; // Referenzzähler = 0 -> Objekt wird zerstört
end;
Anhand dieses Beispiels sieht man, dass man Objekte und ihre Interfaces nicht mischen sollte.
Eine Möglichkeit, dieses Konstrukt trotzdem zum Laufen zu bekommen wäre, den Referenzzähler manuell zu erhöhen:
Delphi-Quellcode:
procedure EinsA;
var
  Obj: TMyClass;
  Intf: IMyInterface;
begin
  Obj := TMyClass.Create; // Referenzzähler = 0
  Obj._AddRef; // Referenzzähler = 1
  Obj.Name := 'Meier';
  Intf := Obj; // Referenzzähler = 2
  Intf.PrintName;
  Obj.MachWasAnderes;
  Intf := nil; // Referenzzähler = 1
  Obj.Free; // Objekt wird zerstört
end;
Eine Sache noch:
Delphi-Quellcode:
procedure Drei;
var
  Intf1, Intf2: IMyInterface;
begin
  Intf1 := TMyClass.Create;
  Intf2 := Intf1;
  if (Intf1 = Intf2) then
    ShowMessage('Diese Meldung wird niemals erscheinen);
Intf2 := nil;
Intf1 := nil;
end;
Interfaces sind niemals identisch. ein Vergleich von Interfaces wird immer scheitern. Um dieses Problem zu beheben müsste jede erzeugte Klasse eine eindeutige ID erhalten, die über das Interface überprüft werden kann. Da verschiedene Interfaces das gleiche Objekt repräsentieren, wäre bei beiden Interfaces auch die ID gleich.

Was sind nun die Vorteile eines Interfaces?
Ein Interface ist sozusagen der Bauplan einer Klasse, den man benutzen kann, ohne die Klasse zu kennen. Das ist insbesondere bei DLLs interessant; eine DLL erzeugt ein Interface und das Hauptprogramm kann damit arbeiten, ohne dass der gesamte Klassen-Quellcode dem Hauptprogramm bekannt ist, da das Hauptprogramm das Interface kennt. Oder ein Hauptprogramm übergibt der DLL ein Interface, und die DLL kann damit arbeiten. Da die DLL nur das Interface kennt, bleibt diese schön klein.



Da ich heute etwas mit meinem neuen Delphi spielen wollte, habe ich mir erlaubt, Deinen Quellcode etwas zu überarbeiten.
Ich habe die einzelnen Klassen in eigene Units gesteckt und aus TKonsole ein Interface gebaut. Außerdem habe ich die Renderlogik aus dem Form genommen und in eine eigene Klasse (auch mit Interface) gesteckt. Programmlogik hat nämlich in einem Form nichts zu suchen, da gehört nur das Programminterface (Eingabe, Ausgabe) rein.
Da die beiden DLLs nur die Interfaces benötigen, sind sie viel kleiner geworden.
Schau Dir das Ganze mal an, vielleicht kanst Du damit ja was anfangen.

Ach ja, um die Anfangsfrage zu beantworten: Ein Fehler kommt bei mir nicht.

Gruß
xaromz
Angehängte Dateien
Dateityp: rar engine_112.rar (472,4 KB, 8x aufgerufen)
  Mit Zitat antworten Zitat