2012-04-02 89 views
1

举行提供尽可能多的信息,我可以当,下面是我在做什么问题与释放接口对象数组中的

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; 

好了,所以创建的TContainingObject实例的非常简单的例子,并依次创建TMyInterfacedObject的实例,存储在IMyInterface的Array中。

TContainingObjectdestructor被调用时,它将无参考并折叠数组。

我遇到的问题是,没有其他任何地方参考,TMyInterfacedObjectdestructor永远不会被调用,从而内存泄漏。

我做错了什么,或者是德尔福的引用计数系统无法应对接口类型数组中保存的接口对象的简单概念吗?

感谢您的任何建议!

更多信息

TContainingObject提供一种阵列属性来访问包含在所述阵列内的IMyInterface单个实例。

在我的实际代码中,多个接口类型之间有循环引用。 我们建议IMyInterface包含一个函数GetSomething: IAnotherInterface,并且IAnotherInterface包含GetMyInterface: IMyInterface(循环引用)。 这可能会导致我的问题?如果是这样,那么通常的参考文件是绝对需要,那么解决方法是什么?

+1

你对TInterfaceList而不是数组有同样的问题吗? – 2012-04-03 00:02:53

+0

我从来没有看过TInterfaceList。我现在来看看,但我真的更喜欢使用一个简单的数组(为了利用我现有的“Managed Array”系统) – LaKraven 2012-04-03 00:06:07

+0

好吧...补充我的数组'TInterfaceList',结果就是相同:'TMyInterfacedObject'的'析构函数'永远不会被调用,内存泄漏! – LaKraven 2012-04-03 00:18:42

回答

5

如果IMyInterface实施包含IAnotherInterface成员,并为IAnotherInterface实施包含IMyInterface成员,他们称对方,那么他们的引用计数将永远无法下降到0,除非引用您明确一个,这可能意味着添加方法到您的界面来做到这一点,例如:

type 
    IAnotherInterface = interface; 

    IMyInterface = interface 
    ['{guid}'] 
    function GetAnotherInterface: IAnotherInterface; 
    procedure SetAnotherInterface(Value: IAnotherInterface); 
    property AnotherInterface: IAnotherInterface read GetAnotherInterface write SetAnotherInterface; 
    end; 

    IAnotherInterface = interface 
    ['{guid}'] 
    function GetMyInterface: IMyInterface; 
    procedure SetMyInterface(Value: IMyInterface); 
    property MyInterface: IMyInterface read GetMyInterface write SetMyInterface; 
    end; 

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; 

现在看的引用计数,当你不explicitally引用的自由之一:

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; 

现在添加一个明确释放到参考之一:

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

我刚刚通过Skype了解了这个问题,就是这样,雷米!他有循环参考! – chuacw 2012-04-03 01:27:48

+0

感谢@RemyLebau和chuacw ......那就做到了! – LaKraven 2012-04-03 01:39:59

0

当我在Delphi XE2中运行你的代码示例时(修复了一个小错字 - 请参阅编辑),它对我来说运行得很好,并按照预期调用了TMyInterfacedObject析构函数。

+0

这就是为什么我很困惑!我在其他代码中做了类似的事情,它工作正常,但在这个特定的单元和另一个(在两个单独的项目中),析构函数永远不会被调用! – LaKraven 2012-04-03 00:23:31

+0

@LaKraven不能是这个单元的不同项目选项或编译器指令吗? – EMBarbosa 2012-04-03 00:40:24

+0

@EMBarbosa no。没有特殊的条件,指令,设置或任何其他类似的东西......它是一个标准单位,在具有默认项目选项的标准项目中。 – LaKraven 2012-04-03 00:42:07