Autor Beitrag
Nightmare_82
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 260



BeitragVerfasst: Mi 26.11.03 01:57 
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:
ausblenden 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; // Eine Funktion die mir den Pointer liefert
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: Mi 26.11.03 11:00 
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?
ausblenden 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.

_________________
Ist Zeit wirklich Geld?
Nightmare_82 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 260



BeitragVerfasst: Mi 26.11.03 14:06 
also, ich würde gerne folgendes machen:
ausblenden 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
{irgendwelche Funktionen}
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;
 {hier werden die Indexe aus einer Datei gelesen und ein IField-Array mit Hilfe der Funktion getField au der Klasse TGame erstellt}
end;


wenn ich nun sowas aufrufe :
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
function GetField(Index: Integer): IField;
begin
  Result := FFields[Index];
  Result.AddRef; // Instanz darf nicht freigegeben werden
end;

_________________
Ist Zeit wirklich Geld?
Nightmare_82 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 260



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

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
// liefert das Interface
function TForm1.getTest : ITest;
begin
  Result := m_oTest;
  Result._AddRef;
  Form1.Caption := IntToStr(m_oTest.RefCount);
end;

procedure TForm1.FormClick(Sender : TObject);
begin
  {Methode aufrufen, die nicht im Interface deklariert ist}
  TTest(getTest).getY;
  // geht nicht :
  //(getTest as TTest).getY;
end;
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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.

_________________
Ist Zeit wirklich Geld?