You can use interfaces and store references to them in classes, and not to objects:
unit EntityInterfaces type IMaster = interface; IDog = interface; IMaster = interface ['{8417E5A8-02FE-4A83-BD0C-F69E79492796}'] function GetDog: IDog; procedure SetDog(const ADog: IDog); property Dog: IDog read GetDog write SetDog; end; IDog = interface ['{9B501AD7-BD77-46AC-BC08-545433EC5FFE}'] function GetMaster: IMaster; procedure SetMaster(const AMaster: IMaster); property Master: IMaster read GetMaster write SetMaster; end;
Classes themselves in this case are declared like this:
TMaster = class(TInterfacedObject, IMaster) private [Weak] FDog: IDog; function GetDog: IDog; procedure SetDog(const ADog: IDog); end; TDog = class(TInterfacedObject, IDog) private [Weak] FMaster: IMaster; function GetMaster: IMaster; procedure SetMaster(const AMaster: IMaster); end;
And work is already underway with the interfaces:
procedure TForm11.btn1Click(Sender: TObject); var Master: IMaster; Dog: IDog; begin Master := TMaster.Create; Dog := TDog.Create; Master.Dog := Dog; Dog.Master := Master; end; // на выходе из метода Master и Dog будут автоматически удалены, // поскольку их "циклические ссылки" друг на друга имеют атрибут Weak // без использования слабых ссылок возникла бы утечка памяти.
The disadvantages of the method:
the need to twice describe the methods - in the interface and directly in the implementation.
Auto-deletion of an object when bringing the reference count to zero will be unusual for those who have not worked with interfaces.
To eliminate the last drawback, you can make your own IUnknown implementation and inherit your classes not from TInterfacedObject, but from TNoReferenceObject:
type TNoReferenceObject = class(TObject, IInterface) { IInterface } function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; { TNoReferenceObject } function TNoReferenceObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE; end; function TNoReferenceObject._AddRef: Integer; begin Result := -1; end; function TNoReferenceObject._Release: Integer; begin Result := -1; end;
Reference counting for all TNoReferenceObject heirs will not work and all created instances will need to be deleted manually, as well as ordinary objects:
procedure TForm11.btn1Click(Sender: TObject); var Master: IMaster; Dog: IDog; begin Master := TMaster.Create; Dog := TDog.Create; try Master.Dog := Dog; Dog.Master := Master; finally TObject(Master).Free; TObject(Dog).Free; end; end;