Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Jul 10, 2025 21:57

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 5 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Interfaces und deren Zerstörung
BeitragVerfasst: Do Okt 30, 2008 14:00 
Offline
DGL Member

Registriert: So Aug 20, 2006 23:19
Beiträge: 564
Hi,
ich habe vor einer Weile mal mit Java angefangen und bin auf die Vorteile von Interfaces gestossen. Kurz darauf habe ich bemerkt, dass man die auch in Delphi verwenden kann und daher habe ich mein bisheriges Projekt, eine 3D Engine, mit Interfaces ausgestattet.

Ich habe einen Eventmanager, der alle Events (Keydown, Mousebewegungen, Klicks, Exit usw usw usw) registriert und weiterleitet. Für jedes Event verfügt er über eine Liste, in der Interfaces abgelegt sind.

Wenn ich nun zb eine Szene erstellen und das Interface IKeyListener implementiere, dann kann ich diese Szene dank ihres Interfaces in meinem Eventlistener registrieren. Wenn mein Eventlistener nun ein KeyDown event registriert, ruft er automatisch die Keydownmethode der Szene, die im Interface definiert ist, auf. Soviel zur Vorgeschichte. hier erstmal etwas Quellcode, damit das klarer wird:

Die Listenerdefinition:
Code:
  1. type
  2.   IKeyListener = interface(IInterface)
  3.     ['{BB9DB2E4-B01D-445E-9778-5742CD29ED7C}']
  4.     procedure OnKeyDown   (event : TKeyEvent);
  5.     procedure OnKeyUp     (event : TKeyEvent);
  6.     procedure OnKeyPressed(event : TKeyEvent);
  7.   end;


Die Szenendefinition:
Code:
  1. type
  2.   TWorldScene = class (TGL_Scene, IKeyListener)
  3.   private
  4.     points: Array of TGLVector3f;
  5.     tmp:    Array of TGLVector3f;
  6.  
  7.   public
  8.     constructor Create();              override;
  9.     procedure Init;                    override;
  10.     procedure Render;                  override;
  11.     procedure Update (aTime: Integer); override;
  12.     procedure Release;                 override;
  13.     destructor Destroy();              override;
  14.  
  15.     procedure OnKeyDown   (event : TKeyEvent);
  16.     procedure OnKeyUp     (event : TKeyEvent);
  17.     procedure OnKeyPressed(event : TKeyEvent);
  18.   end;


Wenn ich nun in meinem Hauptprogramm die Szene per:
Code:
  1.   EventManager.AddKeyListener(world);

registriere, dann wird fortan bei jedem Tastendruck die entsprechenden Methoden aus der Szene aufgerufen..


Nun das Problem:
Bei der Erstellung der Szene muss ich die Szene in meinem Szenenmanager registrieren. Das heisst das Objekt world wird automatisch doppelt registriert. Einmal in meinem Szenenmanager, weil es von Scene erbt und einmal im Eventmanager, weil es den IKeyListener implementiert.

Wenn ich das Programm allerdings beende und alle Objekte zerstört werden, treten Fehler auf, die aus irgendeinem Grund mit den Interfaces zusammenhängen:
Code:
  1. destructor TGL_SceneManager.Destroy;
  2. var i : Integer;
  3. begin
  4.   ReleaseScene();
  5.   for i := 0 to lScenes.Count - 1 do
  6.   begin
  7.     (lScenes.Objects[i] as TGL_Scene).Destroy;
  8.   end;
  9.   lScenes.Destroy;
  10. end;
  11. end.


In diesem Destructor des ScenenManagers werden alle Szenen, die in der THashedStringList lScenes abgelegt sind, zerstört. In dem Moment, in dem das Programm versucht, die Worldscene zu zerstören, bekomme ich eine EInvalidPointer mit der Meldung "Ungültige Zeigeroperation" und zwar genau in der Zeile begin des Destruktors.

Code:
  1. destructor TWorldScene.Destroy;
  2. begin
  3.   inherited Destroy;
  4. end;



Ich bastel hier jetz schon ewig dran rum und weiss einfach nicht warum das nicht geht. Nicht zuletzt könnte das aber daran liegen, dass es bei Interfaces womöglich irgendeine Spezifikation gibt, von der ich nichts weiß. Prinzipiell sind die Quelle, die ich im Internet zu Delphiinterfaces finde, aber ziemlich Bescheiden..


Vielleicht hat ja jemand eine Idee
danke


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Do Okt 30, 2008 14:39 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Also deine Interface Klasse muss von TInterfacedObject abgelitten sein. Und Interfaces haben eine Referenzzählung! Du hast eine Klasse mit einem Interface verbundelst. Wenn du das Interface an 3 Stellen verwendest und diese Stellen dann mit NIL zuweist bzw die Klassen die eine Referenz auf das Interface hat dann freigibst, dann wird automatisch die Instanz deiner Klasse gelöscht. Wenn du in der Zwischenzeit deine Klassen die ein Interface anbieten auf herkömmliche Weise freigibst, dann zeigt dein Interface ins nichts. Wenn das per Zufall noch eine gültigen Referenzzähler hat der irgendwann 0 ist, dann wird versucht eine nicht mehr existierende Instanz zu löschen. Ich vermute mal so etwas wird bei dir passiert sein.

Code:
  1. var
  2.   Blah: IBlah;
  3. begin
  4.   Blah := TBlah.Create;
  5.   Blah.Blub;
  6.   Blah := nil;
  7. end;


So sieht zu mindest der klassische Weg aus. Ich weiß jetzt nicht ob es sinnvolle Wege gibt sowohl die Klasseninstanz als auch das Interface parallel benutzen zu können. Bzw benutzen kann man sie ja aber du würdest nicht mitbekommen, wenn die Klasse durch die Referenzzählung gelöscht würde. Bzw die Interfaces würden nicht merken, wenn die Instanz der Klasse manuell gelöscht würde.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Okt 31, 2008 09:01 
Offline
DGL Member

Registriert: So Aug 20, 2006 23:19
Beiträge: 564
Also ich habe, um versehentliche Freigabe eines nichtmehr existenten Objektes/Interface vorzubeugen, mal alle Destroys auf Free umgestellt. IM Grunde müsste dieses Problem ja nun umgangen werden. Funktionieren tuts aber dennoch nicht ^^


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Okt 31, 2008 09:42 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
"Das glaube ich nicht Tim." ;) Denn das Free überprüft ob die Variable der Instanz ungleich nil ist. Das sind aber nur Pointervariablen. Du kannst aber mehr als einen Pointer auf deine Instanz haben. Kleines Beispiel um das zu verdeutlichen.
Code:
  1. var
  2.   A, B: TList;
  3. begin
  4.   A := TList.Create;
  5.   B := A;
  6.   FreeAndNil(A);
  7.   A.Free; // funktioniert problemlos, da es vom Free ignoriert wird
  8.   B.Free; // zugriffsverletzung
  9. end;

A ist nach FreeAndNil nil. Wenn du A dann noch mal freigeben wolltest würde nichts passieren, da A bereits nil ist. Aber was passiert bei B.Free? B ist nach wie vor gesetzt und zeigt auf den Speicherbereich der vormals A zugewiesen war. Im Falle von Free merkt der Delphispeichermanager aber, dass der Speicher nicht mehr existiert und schmeißt eine Zugriffsverletzung. Du könntest aber zu B "problemlos" Einträge hinzufügen, löschen etc. Knallen würde vermutlich es erst dann, wenn jemand anders den freien Speicher für sich beanspruchen würde. Wenn man anstelle des FreeAndNil 2 Mal A.Free aufrufen würde würde es beim zweiten Mal auch knallen, da nach dem ersten A.Free A auch noch auf den Speicher zeigt.

Genau das Gleiche passiert bei deinen Interfaces! Du gibst die Instanzen der Klasse zwar frei allerdings hast du immer noch einen Pointer auf dessen Speicher (Variablen auf die Interfaces. vergleichbar mit B). Nur, dass du dort den Destruktor nicht selber aufrufst sondern dieser automatisiert von dem Interface aufgerufen wird, wenn dieses nicht mehr benutzt wird. Wenn die Interface nicht gerade eine Referenz auf einander haben (A hat referenz auf B und B eine auf A), dann wird der Destruktor in jedem Fall irgendwann aufgerufen werden. Der hat aber nach wie vor einen eigene Instanzvariable.

Wenn du von einer Interfaceklasse sowohl die Klasseninstanz als auch die Interfaces benutzt, dann darfst du die Klasse nicht selber freigeben. Bzw es kann auch passieren, dass die Klasseninstanz ohne dein Wissen gelöscht wird, wenn kein Interface mehr benutzt wird. Und dann würde dir die Klasse unter dem Allerwertesten weggeschossen werden. Entsprechend solltest du selbst für deine normalen Klassenfunktionen ein Interface definieren und dieses benutzen. Aber nicht mischen. Eine Klasse kann problemlos 2-30 Interface unterstützen. Allerdings solltest du wirklich nur über Interface arbeiten.

PS: Crossposting im Delphi-forum (oder auch anderswo) bitte nächstes Mal selber mit angeben. Das kann es verhindern, dass man doppelte Hinweise schreibt.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Okt 31, 2008 11:29 
Offline
DGL Member
Benutzeravatar

Registriert: Di Okt 03, 2006 14:07
Beiträge: 1277
Wohnort: Wien
Für Delphi 5 gab es eine anständige Dokumentation. Ein Teil davon ist die Object Pascal Sprachreferenz (Object Pascal Language Guide), Kapitel 10, "Schnittstellen". Dieses Buch ist als PDF kostenlos im Internet erhältlich (einfach googeln) und wirklich SEHR informativ.


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 5 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 5 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.012s | 15 Queries | GZIP : On ]