I'll implement an IoC container in the line of VSoft's one. While adding the possibility of doing DI through constructor injection would be great, i won't implement it. It's not a hard requirement of mine and fpc currently does not support the features (basically Delphi's new RTTI) needed to implement it without hacks.
Automatic reference counting
Most of Delphi IoC implementations use COM interfaces and rely on the automatic reference count to manage object instance life cycle. So do i. This approach's drawback is that the class to be instantiated must handle the reference count. When designing new classes or when class hierarchy can be modified, is sufficient to inherit from TInterfaced* classes. The problem rises when is necessary to use a class that has a defined hierarchy and does not handle reference counting, like LCL ones.
Since i plan to decouple TForm descendants, i need a way to use them with the IoC container. Below is the (rough) design, in pseudo code:
//Define interface IPersonView = interface ['{9B5BBA42-E82B-4CA0-A43D-66A22DCC10DE}'] procedure DoIt; end; //Implement an IPersonView TPersonViewForm = class(TForm, IPersonView) procedure DoIt; end; //Register implementation Container.Register(IPersonView, TPersonViewForm); //Instantiate the view Container.Resolve(IPersonView)At first look, it should work seamlessly. And in fact does: a TPersonViewForm is instantiated and returned as IPersonView. The only issue is that the object instance will never be freed even when the interface reference goes out of scope. This occurs because _AddRef and _Release methods of TComponent does not handle reference count by default.
VCLComObject to the rescue
Examining the code, we observe that TComponent _AddRef and _Release forwards to VCLComObject property. There's not good documentation or examples of using this property. So i wrote an example to see if it would solve my problem.
Basically i wrote TComponentReference, a descendant of TInterfacedObject with a dummy implementation of IVCLComObject that gets a TComponent reference in the constructor and free it in BeforeDestruction.
constructor TComponentReference.Create(Component: TComponent); begin FComponent := Component; end; procedure TComponentReference.BeforeDestruction; begin inherited BeforeDestruction; FComponent.Free; end;
And this is how i tested:
function GetMyIntf: IMyIntf; var C: TMyComponent; R: IVCLComObject; begin C := TMyComponent.Create(nil); R := TComponentReference.Create(C); C.VCLComObject := R; Result := C as IMyIntf; end; var MyIntf: IMyIntf; begin MyIntf := GetMyIntf; MyIntf.DoIt; end.
It worked! I get a IMyIntf reference and no memory leaks. Easier than i initially think.
The code can be downloaded here.
No comments:
Post a Comment