Einzelnen Beitrag anzeigen

choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#5

Re: Zugriffverletzung beim Verwenden von Interfaces

  Alt 15. Dez 2003, 00:04
Hallo mirage228,

wenn ich Deine Ausführungen richtig verstanden habe, liegt die Ursache des Problems in der heterogenen Verwendung von Klassen(-Objekten) und (Objekt-)Interfaces.
Bedingt durch das in Delphi verwendete Interface-Konzept wird bei der Arbeit mit Interfaces eine implizite Referenzzählung vorgenommen.

Der folgende Code
Delphi-Quellcode:
var
  myObject: IMyInterface
begin
  myObject:= GetAnObject;
  myObject.AMethod;
  myObject:= GetAnotherObject;
  myObject.AnotherMethod;
end;
wird deshalb vom Compiler um Code ergänzt, den man etwa so schreiben könnte
Delphi-Quellcode:
begin
  myObject:= GetAnObject;
  myObject._AddRef;
  myObject.AMethod;
  myObject._Release;
  myObject:= GetAnotherObject
  myObject._AddRef;
  myObject.AnotherObject;
  myObject._Release;
end;
Tatsächlich variiert der Aufruf von _Release ein wenig, so dass in diesem Beispiel die Methode erst nach dem Aufruf von GetAnotherObject aufgerufen wird, darüber hinaus sollte man sich die Referenzzählung von try..finally-Blöcken umschlossen vorstellen, der Einfachheit halber habe ich das aber vernachlässigt.

Betrachtet man nun die Implementierung von _Release in dem von Dir verwendeten Vorfahren TInterfacedObject:
Delphi-Quellcode:
function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;
Erkennt man, dass Objekte dieses Typs freigegeben werden, sobald der Referenzzähler null erreicht hat. Weil ein solches Objekt nach dem Verlassen des Konstruktors mit null belegt ist führt dieser Code
Delphi-Quellcode:
var
  myClassicalObject: TInterfacedObject;
  myInterfacedObject: IInterface;
begin
  myClassicalObject:= TInterfacedObject.Create;
  myInterfacedObject:= myClassicalObject; // implicit call of _AddRef
  myInterfacedObject:= nil; // implicit call of _Release -> Free;

  // !myClassicalObject contains an invalid reference, now
  Showmessage(IntToStr(myClassicalObject.RefCount));
end;
zu Problemen (dass der Code fehlerfrei funktionieren kann, liegt an der Speicherverwaltung von Delphi, führt aber spätestens bei mehreren parallelen Verarbeitungssträngen zu Problemen).

Wenn Du Dich mit diesem Phänomen eingehender beschäftigen möchtest, empfehle ich Dir, eine Testklasse zu implementieren, die die Methoden _AddRef und _Release sowie den Aufruf des Destruktors protokolliert, bzw den Code im integrierten Debugger mit Debug-DCUs und einem Nachfahren von TInterfacedObject mit Breakpoints in den entsprechenden Zeilen der Unit System zu analysieren.

Lösen lassen sollte sich das Problem relativ einfach, indem Du entweder ausschließlich "klassische Objekte" oder Interfaces verwendest. Listen für den letzteren Fall lassen sich dann zB mithilfe von TInterfaceList realisieren...
gruß, choose
  Mit Zitat antworten Zitat