1

Um so viele Informationen wie möglich gehalten, hier ist ein sehr einfaches Beispiel dafür, was ich tueProblem mit Freigabe Objekten Interfaced wenn sie in einem Array

type 
    IMyInterface = interface 
    [THE_GUID_HERE] 
    // some methods 
    end; 

    TMyInterfaceArray = Array of IMyInterface; 

    TMyInterfacedObject = class(TInterfacedObject, IMyInterface) 
    // implementation of the Interface etc. here 
    end; 

    TContainingObject = class 
    private 
    FIObjectArray: TMyInterfaceArray; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure NewInstanceOfInterfacedObject; 
    end; 

    implementation 

    constructor TContainingObject.Create; 
    begin 
    inherited; 
    // Just to illustrate that an Instance is being created... 
    NewInstanceOfInterfacedObject; 
    end; 

    destructor TContainingObject.Destroy; 
    var 
    I: Integer; 
    begin 
    for I := Low(FIObjectArray) to High(FIObjectArray) do 
     FIObjectArray[I] := nil; 
    SetLength(FIObjectArray, 0); // Array collapsed 

    inherited; 
    end; 

    procedure TContainingObject.NewInstanceOfInterfacedObject; 
    var 
    LIndex: Integer; 
    begin 
    LIndex := Length(FIObjectArray); 
    SetLength(FIObjectArray, LIndex + 1); 
    FIObjectArray[LIndex] := TMyInterfacedObject.Create; 
    end; 

Okay, also eine Instanz von TContainingObject erstellt wird, und erzeugt wiederum eine Instanz von TMyInterfacedObject, gespeichert in einem Array von IMyInterface.

Wenn TContainingObjectdestructor aufgerufen wird, ist es die Referenz und bricht das Array zusammen.

Das Problem, das ich habe, ist, dass, ohne andere Referenzen nirgendwo, TMyInterfacedObject 's destructor wird nie aufgerufen, und damit Speicherlecks.

Mache ich etwas falsch, oder ist das Referenzzählsystem von Delphi nicht in der Lage, das einfache Konzept von Interfaced Objects zu bewältigen, das in einem Array des Interface-Typs gehalten wird?

Danke für jede Beratung!

Weitere Informationen

TContainingObject bietet eine Array-Eigenschaft für die einzelnen Instanzen IMyInterface im Array enthaltenen zuzugreifen.

In meinem tatsächlichen Code gibt es Zirkelverweise zwischen mehreren Schnittstellentypen. Angenommen, IMyInterface enthält eine Funktion GetSomething: IAnotherInterface, und IAnotherInterface enthält GetMyInterface: IMyInterface (eine Zirkularreferenz). Könnte das mein Problem verursachen? Wenn ja, ist die Kreisreferenz absolut erforderlich, also was wäre eine Lösung in diesem Sinne?

+1

Haben Sie das gleiche Problem mit einer TInterfaceList anstelle eines Arrays? –

+0

Ich habe noch nie TInterfaceList angeschaut.Ich werde jetzt einen Blick darauf werfen, aber ich würde wirklich lieber ein einfaches Array verwenden (um meine bestehenden "Managed Array" -Systeme zu nutzen) – LaKraven

+0

Okay ... mein Array für 'TInterfaceList' ergänzt, und das Ergebnis ist genau das selbe: 'destructor' von' TMyInterfacedObject' wird nie aufgerufen, Speicherlecks! – LaKraven

Antwort

5

Wenn die Implementierung für IMyInterface enthält ein IAnotherInterface Mitglied, und die Umsetzung für IAnotherInterface enthält ein IMyInterface Mitglied, und sie beziehen sich auf einander, dann ihre Referenzzähler niemals in der Lage sein, auf 0, wenn Sie klar eine der Referenzen fallen Dies bedeutet wahrscheinlich das Hinzufügen von Methoden zu Ihren Schnittstellen, um dies zu tun, zB:

.

type 
    TMyInterface = class(TInterfacedObject, IMyInterface) 
    private 
    FAnotherInterface: IAnotherInterface; 
    public 
    function GetAnotherInterface: IAnotherInterface; 
    procedure SetAnotherInterface(Value: IAnotherInterface); 
    end; 

    TAnotherInterface = class(TInterfacedObject, IAnotherInterface) 
    private 
    FMyInterface: IMyInterface; 
    public 
    function GetMyInterface: IMyInterface; 
    procedure SetMyInterface(Value: IMyInterface); 
    end; 

    function TMyInterface.GetAnotherInterface; 
    begin 
    Result := FAnotherInterface; 
    end; 

    procedure TMyInterface.SetAnotherInterface(Value: IAnotherInterface); 
    begin 
    if FAnotherInterface <> Value then 
    begin 
     if FAnotherInterface <> nil then FAnotherInterface.SetMyInterface(nil); 
     FAnotherInterface := Value; 
     if FAnotherInterface <> nil then FAnotherInterface.SetMyInterface(Self); 
    end; 
    end; 

    function TAnotherInterface.GetMyInterface: IMyInterface; 
    begin 
    Result := FMyInterface; 
    end; 

    procedure TAnotherInterface.SetMyInterface(Value: IMyInterface); 
    begin 
    if FMyInterface <> Value then 
    begin 
     if FMyInterface <> nil then FMyInterface.SetAnotherInterface(nil); 
     FMyInterface := Value; 
     if FMyInterface <> nil then FMyInterface.SetAnotherInterface(Self); 
    end; 
    end; 

nun die Referenzzähler sehen, wenn Sie nicht explicitally kostenlos eine der Referenzen tun:

var 
    I: IMyInterface; 
    J: IAnotherInterface; 
begin 
    I := TMyInterface.Create; // I.RefCnt becomes 1 
    J := TAnotherInterface.Create; // J.RefCnt becomes 1 
    I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2 
    ... 
    I.AnotherInterface := nil; // I.RefCnt becomes 1, J.RefCnt becomes 1 
    { 
    // implicit when scope is cleared: 
    I := nil; // I.RefCnt becomes 0, I is freed 
    J := nil; // J.RefCnt becomes 0, J is freed 
    } 
end; 

:

var 
    I: IMyInterface; 
    J: IAnotherInterface; 
begin 
    I := TMyInterface.Create; // I.RefCnt becomes 1 
    J := TAnotherInterface.Create; // J.RefCnt becomes 1 
    I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2 
    ... 
    { 
    // implicit when scope is cleared: 
    I := nil; // I.RefCnt becomes 1, I is NOT freed 
    J := nil; // J.RefCnt becomes 1, J is NOT freed 
    } 
end; 

nun eine explizite Freigabe auf eine der Referenzen hinzufügen.

var 
    I: IMyInterface; 
    J: IAnotherInterface; 
begin 
    I := TMyInterface.Create; // I.RefCnt becomes 1 
    J := TAnotherInterface.Create; // J.RefCnt becomes 1 
    I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2 
    J := nil; // I.RefCnt still 2, J.RefCnt becomes 1, J is NOT freed yet 
    ... 
    I.AnotherInterface := nil; // I.RefCnt becomes 1, J.RefCnt becomes 0, J is freed 
    { 
    // implicit when scope is cleared: 
    I := nil; // I.RefCnt becomes 0, I is freed 
    } 
end; 
+0

Ich habe mir das Problem über Skype angeschaut, und das war es, Remy! Er hat Zirkelverweise! – chuacw

+0

Dank @RemyLebau und chuacw ... das hat den Trick! – LaKraven

0

Wenn ich Ihr Codebeispiel in Delphi XE2 (nach dem Reparieren eines kleinen Tippfehlers - siehe bearbeiten), läuft es gut für mich und ruft die TMyInterfacedObject Destruktor wie erwartet.

+0

Deshalb bin ich so verwirrt! Ich mache eine ähnliche Sache in anderem Code und es funktioniert gut, doch in dieser speziellen Einheit und einem anderen (in zwei separaten Projekten) wird der Destruktor niemals aufgerufen! – LaKraven

+0

@LaKraven könnte nicht verschiedene Projektoptionen oder Compiler-Direktiven für dieses Gerät sein? – EMBarbosa

+0

@EMBarbosa Nr. Es gibt keine besonderen Bedingungen, Anweisungen, Einstellungen oder ähnliches. Es ist eine Standardeinheit in einem Standardprojekt mit Standard-Projektoptionen. – LaKraven

Verwandte Themen