Dealing with circular strong references in Delphi

后端 未结 4 1114
隐瞒了意图╮
隐瞒了意图╮ 2021-01-18 16:57

I got two classes (in my example TObject1 and TObject2) which know each other via interfaces (IObject1, IObject2). As you probably know in Delphi this will lead to a memory

4条回答
  •  失恋的感觉
    2021-01-18 17:41

    It looks like you want both objects to share their reference count. You could do that by letting a third object (TPair) handle the reference counting. A nice way to accomplish this is by using the implements keyword. You can choose to keep this third object hidden, or to interact with that as well.

    With the code below you can either create a TPairChildA, a TPairChildB or their 'parent' TPair. Any of them will create the others when needed and all created objects will be kept alive until none are referenced anymore. You can of course add interfaces like your IObject1 to the objects, but I kept them out for simplicity.

    unit ObjectPair;
    
    interface
    
    type
      TPairChildA = class;
      TPairChildB = class;
    
      TPair = class( TInterfacedObject )
      protected
        FChildA : TPairChildA;
        FChildB : TPairChildB;
    
        function GetChildA : TPairChildA;
        function GetChildB : TPairChildB;
      public
        destructor Destroy; override;
    
        property ChildA : TPairChildA read GetChildA;
        property ChildB : TPairChildB read GetChildB;
      end;
    
      TPairChild = class( TObject , IInterface )
      protected
        FPair : TPair;
    
        property Pair : TPair read FPair implements IInterface;
      public
        constructor Create( APair : TPair = nil ); virtual;
      end;
    
      TPairChildA = class( TPairChild )
      protected
        function GetSibling : TPairChildB;
      public
        constructor Create( APair : TPair = nil ); override;
    
        property Sibling : TPairChildB read GetSibling;
      end;
    
      TPairChildB = class( TPairChild )
      protected
        function GetSibling : TPairChildA;
      public
        constructor Create( APair : TPair = nil ); override;
    
        property Sibling : TPairChildA read GetSibling;
      end;
    
    implementation
    
    //==============================================================================
    // TPair
    
    destructor TPair.Destroy;
    begin
      FChildA.Free;
      FChildB.Free;
      inherited;
    end;
    
    function TPair.GetChildA : TPairChildA;
    begin
      if FChildA = nil then
        FChildA := TPairChildA.Create( Self );
      Result := FChildA;
    end;
    
    function TPair.GetChildB : TPairChildB;
    begin
      if FChildB = nil then
        FChildB := TPairChildB.Create( Self );
      Result := FChildB;
    end;
    
    // END TPair
    //==============================================================================
    // TPairChild
    
    constructor TPairChild.Create( APair : TPair = nil );
    begin
      if APair = nil then
        FPair := TPair.Create
      else
        FPair := APair;
    end;
    
    // END TPairChild
    //==============================================================================
    // TPairChildA
    
    constructor TPairChildA.Create( APair : TPair = nil );
    begin
      inherited;
      FPair.FChildA := Self;
    end;
    
    function TPairChildA.GetSibling : TPairChildB;
    begin
      Result := FPair.ChildB;
    end;
    
    // END TPairChildA
    //==============================================================================
    // TPairChildB
    
    constructor TPairChildB.Create( APair : TPair = nil );
    begin
      inherited;
      FPair.FChildB := Self;
    end;
    
    function TPairChildB.GetSibling : TPairChildA;
    begin
      Result := FPair.ChildA;
    end;
    
    // END TPairChildB
    //==============================================================================
    
    end.
    

    A usage example:

    procedure TForm1.Button1Click( Sender : TObject );
    var
      objA : TPairChildA;
      ifA , ifB : IInterface;
    begin
      objA := TPairChildA.Create;
      ifA := objA;
      ifB := objA.Sibling;
      ifA := nil;
      ifB := nil; // This frees all three objects.
    end;
    

提交回复
热议问题