Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Frage zu Interfaces
Nightmare_82 - Mi 26.11.03 01:57
Titel: Frage zu Interfaces
Ich habe vor längerer Zeit Interfaces getestet und hatte das problem, daß die Referenzzählung nicht richtig funktioniert, wenn man lokale Variablen(Pointer) vom Interface deklariert und zuweist.
Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| ITest = interface function getX : integer; end;
TTest = class(TInterfacedObject, ITest) public function getX : integer; end;
procedure foo; VAR Test : ITest; begin Test:= GetTest; end; |
dabei wird der ref-Count erniedrigt, obwohl die variable nur lokal ist.
Deshalb bin ich auf abstrakte Klassen umgestiegen.
Mich würde aber mal folgendes interessieren:
- kann man den "Bug" fixen?
- Wie performancelastig ist die Benutzung von Interfaces, wenn ich
die Referenzzählung weglasse(AddRef und RemoveRev leer lasse)
AndyB - Mi 26.11.03 11:00
Titel: Re: Frage zu Interfaces
Nightmare_82 hat folgendes geschrieben: |
und hatte das problem, daß die Referenzzählung nicht richtig funktioniert, wenn man lokale Variablen(Pointer) vom Interface deklariert und zuweist. |
Und was funktionierte nicht bzw. was waren die Auswirkungen der angeblich falschen Referenzzähler?
Zitat: |
Variablen(Pointer) |
Hast du etwa folgendes gemacht?
Delphi-Quelltext
1: 2: 3: 4:
| var p: Pointer; begin p := GetMyInterface; end; |
Wenn ja, dann ist es kein Wunder das der Referenzzähler durcheinanderkommt. Denn ein Pointer wird vom Compiler als Zeiger behandelt und nicht als Interface. Somit fügt der Compiler keine AddRef und _Release Aufrufe ein.
Wenn du alle möglichen Interfaces zurückliefern möchtest, kannst du anstatt Pointer den Typ IInterface verwenden.
Das Konvertieren eines Interfaces in einen Pointer nutzt man nämlich normalerweise, wenn man die Referenzzählung umgehen will. Was z.B. bei zirkulären Referenzen von Klassen vorkommt.
Nightmare_82 - Mi 26.11.03 14:06
also, ich würde gerne folgendes machen:
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:
| IField = interface
end;
IGame = interface
function getField(Index : Integer) : IField;
end;
TGame = class(TInterfacedObject, IGame); private FFields : array of TField; public function getField(Index : Integer) : IField; end;
TWorld = class private FGame : IGame; FMap : array of array of IField; public procedure load; end; |
wenn ich nun sowas aufrufe :
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| procedure TWorld.load; VAR TmpField : IField; begin TmpField:= FGame.getField(0); end; |
wird der refCount um eins erniedrigt und das würde ich gerne begründet haben. Kann es sein, daß ich in TGame den Array von IField und nicht von TField deklarieren muß?(das wäre halt nervig weil ich dann immer casten muß)
AndyB - Mi 26.11.03 20:19
Nightmare_82 hat folgendes geschrieben: |
Kann es sein, daß ich in TGame den Array von IField und nicht von TField deklarieren muß? |
Genau das ist der Punkt. Wenn du nämlich dem Result (:IField) ein TField zuweist, wird der Referenzzähler auf 1 gesetzt, da nun eine Interface-Referenz auf die Instanz zeigt. Nachdem dann TmpField nicht mehr benötigt wird, dekrementiert der Compiler TmpField um 1, wodurch RefCount auf 0 sinkt und die Instanz mit Destroy freigegeben wird.
Zitat: |
das wäre halt nervig weil ich dann immer casten muß |
Du kannst kein Interface zu einer Klasse casten. Andersherum ist es möglich. Was du aber machen kannst, da du die Instanzen selbst verwalten (also auch freigeben) willst, ist, dass du den Referenzzähler zum Compiler generierten Code um eins erhöhst.
Delphi-Quelltext
1: 2: 3: 4: 5:
| function GetField(Index: Integer): IField; begin Result := FFields[Index]; Result.AddRef; end; |
Nightmare_82 - Mi 26.11.03 20:36
danke für die Erklärungen!
Aber man kann übrigens Interfaces zu Klassen casten, man kann nur nicht den
AS-Operator benutzen
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| function TForm1.getTest : ITest; begin Result := m_oTest; Result._AddRef; Form1.Caption := IntToStr(m_oTest.RefCount); end;
procedure TForm1.FormClick(Sender : TObject); begin TTest(getTest).getY; end; |
AndyB - Mi 26.11.03 22:21
Nightmare_82 hat folgendes geschrieben: |
Aber man kann übrigens Interfaces zu Klassen casten |
Man kann alles in alles casten. Nur über die Folgen sollte man sich im klaren sein. Ein Interface hat einen ganz anderen Aufbau als eine Klasse. Dein Aufruf mit TTest(getTest).getY funktioniert nur, solange du nicht virtuelle oder dynamische Methoden aufruft, oder auf Felder/Eigenschaften zugreifst. Machst du dies trotzdem wirst du dich vor Schutzverletzungen nicht mehr retten können.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!