0
votes

I want to create an Tinterfacedobject that contains (ao) two properties ObjectLinks and ObjectBacklinks. Objectlinks contains interface references to other objects, ObjectBacklinks contains the reverse of those links

Both properties consist of a TWeakIntfDictionary = class(TDictionary) The Backlinks property is maintained in the ObjectLinks.ValueNotify event

To make sure the interface referecnes are removed from the dictionary when the original object is free'd, a notification algorith (same as TFmxObject uses) is put into place

As suspected I'm running into all kinds of circular reference problems when creating so many references to the same interfacedobjects :( but i can't seem to get out of this problem. When the FreeNotification is called from the object being destroyed, everything goes fine until it return fro the FreeNOtification. At that point the .Destroy of the object is called again :-(

 {1 Dictionary of interfaces (using weak references) using Free notification }
  TWeakIntfDictionary = class(TDictionary<int64, IInterface>, IFreeNotificationMonitor)
  protected
    procedure ValueNotify(const Value: IInterface; Action: TCollectionNotification); override;
  public
    procedure FreeNotification(aObject: TObject);
  end;

  implementation

  procedure TWeakIntfDictionary.FreeNotification(aObject: TObject);
  var
    lObj: TPair<int64, IInterface>;
  begin
    //Object is going to be destroyed, remove it from dictionary
    for lObj in Self do
    begin
      if (lObj.Value as TObject).Equals(aObject) then
      begin
        Remove(lObj.Key);
        Break;
      end;
    end;
  end;

  procedure TWeakIntfDictionary.ValueNotify(const Value: IInterface; Action: TCollectionNotification);
  var
    lDestrIntf: IFreeNotificationBehavior;
  begin
      // When a TObject is added to the dictionary, it must support IDestroyNotification
      // This dictionary is than added to the notificationlist of the TObject
    if Supports(Value, IFreeNotificationBehavior, lDestrIntf) then
      case Action of
        cnAdded:      begin
                        lDestrIntf.AddFreeNotify(Self);
                        lDestrIntf._Release;
                      end;
        cnRemoved,
        cnExtracted:  begin
                        lDestrIntf.RemoveFreeNotify(Self);
                      end;
      end
    else
      raise EWeakInftDictionaryException.Create('Object added to TWeakIntfDictionary does not support IFreeNotificationBehavior');

    inherited;

  end;

Anyone know of a existing implementation of a WeakReferences Dictionary? Anyone any suggestions how to solve this ?

1
If those are the only references, you don't need weak references. You need weak references when A refers to B and B refers to A. What you have described is a container full of references to A and B. Nothing circular there.David Heffernan
You didn't say any of this before now. I can't see any evidence of the list being doubly linked. I can't see any cicularity in the question.David Heffernan
Second sentence: "Objectlinks contains interface references to other objects, ObjectBacklinks contains the reverse of those links"Bascy
That's not circular. You need A to hold reference to B and B to hold reference to A. You've got C holding references to A and B. Or do A and B hold references to C?David Heffernan
Anyway, if that's what you have, make the container hold Pointer rather than an interface type.David Heffernan

1 Answers

0
votes

Found the solution in the following code

procedure TWeakIntfDictionary.FreeNotification(aObject: TObject);
var
  ...
begin
  //Object is going to be destroyed, remove it from dictionary
  lSavedEvent := FDict.OnValueNotify;
  FDict.OnValueNotify := nil;
  lRemoveList := TList<TKey>.Create;
  try
    for lPair in FDict do
    begin
      pointer(lIntf) := lPair.Value;
      if (lIntf as TObject) = aObject then
        lRemoveList.Add(lPair.Key);
    end;
    pointer(lIntf):=nil; // avoid _release for the last item

    for lKey in lRemoveList do
      FDict.Remove(lKey);

  finally
    FDict.OnValueNotify := lSavedEvent;
    lRemoveList.Free;
  end;
end;