I have a small problem with interfaces. Here it is in Pseudo code :
type
Interface1 = interface
end;
Interface2 = interface
end;
TParentClass = class(TInterfacedObject, Interface1)
private
fChild : Interface2;
public
procedure AddChild(aChild : Interface2);
end;
TChildClass = class(TInterfacedObject, Interface2)
private
fParent : Interface2;
public
constructor Create(aPArent : Interface1);
end;
Can anyone see the flaw? I need the child to have a reference to it's parent, but the reference counting doesn't work in this situation. If I create a ParentClass instance, and add a child, then the parent class is never released. I can see why. How do I get round it?
A reference-counted reference has two semantics: it acts as a share of ownership as well as a means of navigating the object graph.
Typically, you don't need both of these semantics on all links in a cycle in the graph of references. Perhaps only parents own children, and not the other way around? If that is the case, you can make the child references to the parent weak links, by storing them as pointers, like this:
TChildClass = class(TInterfacedObject, Interface2)
private
fParent : Pointer;
function GetParent: Interface1;
public
constructor Create(aPArent : Interface1);
property Parent: Interface1 read GetParent;
end;
function TChildClass.GetParent: Interface1;
begin
Result := Interface1(fParent);
end;
constructor TChildClass.Create(AParent: Interface1);
begin
fParent := Pointer(AParent);
end;
This is safe if the root of the tree of instances is guaranteed to be kept alive somewhere, i.e. you are not relying on only keeping a reference to a branch of the tree and still being able to navigate the whole of it.
Well, the reference counting of course does work in this situation - it just doesn't solve the problem.
That's the biggest problem with reference counting - when you have a circular reference, you have to explicitely 'break' it (set one interface reference to 'nil', for example). That's also why reference counting is not really a replacement for garbage collection - garbage collectors are aware that cycles may exist and can release such cyclic structures when they are not referenced from the 'outside'.
You must make a method that explicitly unlinks the right references. There is no way to get the automatic reference counting working properly in this case.
With the use of a function pointer in the first example then the cyclic reference problem doesn't exist. .NET uses delegates, and VB6 uses events. All of which have the benefit of not incrementing the reference count of the object being pointed too.
Related
I have a method that returns a IInterface, the method contain a dictionary created like this:
Dictionary:= TDictionary<string, IInterface>.Create;
That contains:
Dictionary.Add('key', TPerson.Create(DatabaseCon)as IInterface);
When I quit my ongoing program I get an error message memory leaks on the object Tperson.
I've tried in the class destructor method the following:
private //Global variabels
Person:TPerson;
I: IInterface;
begin // in the destructor method of the class
Person:= IInterface as TPerson;
Person.Free;
end;
But I still get the error... How do I release the TPerson.Create(DatabaseCon) as IInterface)?
Or would it be better to save the TPerson as a string in an INI file and then drag the string name out and turn it into an object .. But how??
The structure of the program:
Inf1= Interface(IInterface)
//some functions
Inf2=Interface(Inf1)
//Some functions
TPerson= class(TInterfacedPersistent,Inf2)
// All my functions.
I can get it to work if I:
Dictionary.Add ('key', Person) as IInterface);
Where I in the Create method of the class write:
Person:= TPerson.Create(DatabaseCon);
and in the destructor method writes:
Person.Free;
but is it not wrong to do so? since the class person must first be instantiated when I access the dictionary?
The most likely explanation is that TPerson does not use interface reference counting to manage lifetime. To be sure we'd need to know exactly how TPerson implements IInterface. Unfortunately that key detail was omitted from the question.
If TPerson derives from TInterfacedObject, and so has lifetime managed by reference counting, then your code will not leak. So, I'm reasonably confident that the problem is that your class's implementation of IInterface does not free the object when its reference count goes to zero.
Assuming I am correct, then you have a couple of options to resolve the issue:
Change your class to implement interface reference counted lifetime management.
Use TObjectDictionary<TKey,TValue> rather than TDictionary<TKey,TValue>. Arrange that the value type TValue is TPerson rather than IInterface. And arrange that the dictionary owns its values so that it can free them when they are removed.
Update: Your update shows that my hunch was correct. Your class does not manage lifetime with interface reference counting.
Here I have a tricky situation, I guess. I need to be able to free an object which is a field of a record. I would normally write the cleanup code in the destructor, if it was a class. But since record types can't introduce a "destructor", how would it be possible to call TObject(Field).Free; ?
There'll be two types of usage I predict:
Replacing the record with a new one.
I think this usage would be easy to implement. Since records are value types and so they are copied on assignment, I can overload the assigning operator and free the objects owned by old record.
( Edit: Assignment overloading wasn't able. That's a new info to me.. )
Exiting the scope where record variable defined.
I can think of a private method that frees the objects and this method could be called on scope excitation manually. BUT, here is the same question: How to make it more recordly? This behaviour kind of feels like a class...
Here is a sample (and obviously not the intended usage):
TProperties = record
... some other spesific typed fields: Integers, pointers etc..
FBaseData: Pointer;
FAdditionalData: TList<Pointer>;
//FAdditionalData: array of Pointer; this was the first intended definition
end;
Assume,
FAdditionalData:=TList<Pointer>.Crete;
called in record constructor or manually in record variable scope by accessing the field publicly like
procedure TFormX.ButtonXClick(Sender: TObject);
var
rec: TProperties;
begin
//rec:=TProperties.Create(with some parameters);
rec.FAdditionalData:=TList<Pointer>.Create;
//do some work with rec
end;
After exiting the ButtonClick scope the rec is no more but a TList still keeps its existance which causes to memory leaks...
If all you have in the record is an object reference, then you can't get the compiler to help you. You are in sole charge of the lifetime of that object. You cannot overload the assignment operator, and you don't get any notification of scope finalisation.
What you can do though is to add a guard interface that will manage the lifetime of the object.
TMyRecord = record
obj: TMyObject;
guard: IInterface;
end;
You need to make sure that TMyObject manages its lifetime by reference counting. For example by deriving from TInterfacedObject.
When you initialise the record you do this:
rec.obj := TMyObject.Create;
rec.guard := rec.obj;
At this point, the guard field of the record will now manage your object's lifetime.
In fact, if you want to push this idea further, you can build a dedicated class to guard the lifetime of objects. That then no longer constrains you to implement IInterface on your class. There are plenty of examples on the web that illustrate the technique. For example I offer Jarrod Hollingworth's article titled Smart Pointers, and Barry Kelly's titled Reference-counted pointers, revisited. There are many more out there. It's an old trick!
Note however, that what you have here is a strange hybrid of value type and reference type. On the face of it, records are value types. However, this one acts like a reference type. If you have other fields in the record that are value types then that would be even more confusing. You'll need to be very aware of this issue when you work with such a record.
On the face of it, without knowing more about your design, I'd be inclined to advise you not to put object references in records. They fit better inside reference types, i.e. classes.
I remember that someone created a class named TLifetimeWatcher. Basically, it looks like:
TLifetimeWatcher = class(TInterfacedObject)
private
fInstance: TObject;
fProc: TProc;
public
constructor Create(instance: TObject); overload;
constructor Create(instance: TObject; proc: TProc); overload;
destructor Destroy; override;
end;
// The (cleanup) proc will be executed in the destructor if assigned, otherwise the instance will be freed by invoking the Free method.
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Custom_Managed_Records
THeaderStruct = record
private
public
class operator Initialize (out Header: THeaderStruct);
class operator Finalize (var Header: THeaderStruct);
end;
What are pros and cons of duplication an object instance with constructor or instance function?
Example A:
type
TMyObject = class
strict private
FField: integer;
public
constructor Create(srcObj: TMyObject); overload;
//alternatively:
//constructor CreateFrom(srcObj: TMyObject);
property Field: integer read FField;
end;
constructor TMyObject.Create(srcObj: TMyObject);
begin
inherited Create;
FField := srcObj.Field;
end;
Example B:
type
TMyObject = class
strict private
FField: integer;
public
function Clone: TMyObject;
property Field: integer read FField;
end;
function TMyObject.Clone: TMyObject;
begin
Result := TMyObject.Create;
Result.FField := FField;
end;
One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.
Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)
EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.
newObj := TMyObject.Create(oldObj)
vs.
newObj := oldObj.Clone;
EDIT2 or "Why I want single-line operation"
I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.
I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.
IOW, I prefer to write
Send(TMyObject.Create(obj));
or
Send(obj.Clone);
to
newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class
The Delphi way (TPersistent) separates creation and cloning:
dest := TSomeClass.Create;
dest.Assign(source);
and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.
edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)
type
TBaseSomeObject = class;
TBaseObjectClass = class of TBaseSomeObject;
TBaseSomeObject = class(TPersistent)
function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
end;
...
function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
begin
if Assigned(t) then
Result := t.Create
else
Result := TBaseObjectClass(Self.ClassType).Create;
Result.Assign(Self);
end;
SendObject(obj.Clone); // full clone.
SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object
For the rest, just implement your assign() operators, and you can mix multiple ways.
edit2
I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.
I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)
The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
The clone way is short although you need to provide a call for each class.
The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.
And if you implement Assign using streams, you have only one place to worry about which fields need to be available.
I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.
I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.
I have designed a hierarchy, every class has 2 readonly properties mapped to 2 private fields.
Every class has a cosntructor that inherits the parent class one.
The problem is that at every level of hierarchy the number of parameters increase of 2:
TBaseClass.Create (par1, par2);
TSubClass.Create(par1, par2, par3, par4);
TSubSubClass.Create(par1, par2, par3, par4, par5, par6);
[...]
Is it ok to have constructors with 6-8 parameteres? After creation my objects should be immutable, so this is why I try to initialize all fileds in the constructors.
Is there another technique you can suggest or should I go with the above mentioned approach? Thanks.
As long as they're well-documented, I've never had any stigma against functions with large numbers of parameters. So an 8-param constructor wouldn't scare me.
However, I can see where the explosion-of-params could occur here, especially if you start adding more than 2 properties per object. I could also see an uncomfortable proliferation of constructor overloads, if some of those params can be defaulted/optional.
With that in mind, you might want to encapsulate the complexities of setting all those params, by using a construction pattern. Builder comes to mind, though Factory or Prototype might also be useful.
Best to stick with conventions when you can. A Builder implies a sequential or multiple object creation process. Abstract Factory is more appropriate for creating a single object from a hierarchy.
Having said that, the regularity does seem a little odd. Does SubClass3 in fact need all 6 properties or only it's two? Remember LSP - SubClass3 is supposed to be completely substitutible for BaseClass so at each level the new ancestor assumes responsibility for the entire set, which is usually more than just passing them back through constructors.
Why don't you let those properties be writable and protect the object by casting it to an interface? Like this:
type
IMyObject = interface
function GetProperty1(): integer;
function GetProperty2(): boolean;
end;
TMyObject = class(TInterfacedObject, IMyObject)
public
constructor Create();
function GetProperty1(): integer;
function GetProperty2(): boolean;
procedure SetProperty1(Value: integer);
procedure SetProperty2(Value: boolean);
end;
function CreateMyObject: IMyObject;
var obj: TMyObject
begin
obj := TMyObject.Create;
obj.SetProperty1(45);
obj.SetProperty2(false);
Result := obj;
end;
This Delphi code will show a memory leak for an instance of TMyImplementation:
program LeakTest;
uses
Classes;
type
MyInterface = interface
end;
TMyImplementation = class(TComponent, MyInterface)
end;
TMyContainer = class(TObject)
private
FInt: MyInterface;
public
property Impl: MyInterface read FInt write FInt;
end;
var
C: TMyContainer;
begin
ReportMemoryLeaksOnShutdown := True;
C := TMyContainer.Create;
C.Impl := TMyImplementation.Create(nil);
C.Free;
end.
If TComponent is replaced by TInterfacedObject and the constructor changed to Create(), the leak disappears. What is different with TComponent here?
Many thanks for the answers. To sum up: it is easy, but wrong, to say "If you are using interfaces, they are reference counted and hence they are freed for you." - Actually any class which implements an interface can break this rule. (And there will be no compiler hint or warning be shown.)
Differences in implementation
TComponent._Release does not free your instance.
TInterfacedObject._Release does free your instance.
Perhaps someone can chime in but my take on this is that TComponent is not meant to be used as a reference counted object the way we normally use interfaces.
Implementation of TComponent._Release
function TComponent._Release: Integer;
begin
if FVCLComObject = nil then
Result := -1 // -1 indicates no reference counting is taking place
else
Result := IVCLComObject(FVCLComObject)._Release;
end;
TComponent doesn't implement its _AddRef and _Release methods the same as TInterfacedObject does. It defers its reference counting to its VCLComObject property, which should be some other interfaced object. Since TComponent doesn't count references, it can't detect when its reference count reaches zero, so it doesn't free itself.
The VCLComObject property holds an interface reference, which should implement IVCLComObject. If a component's associated VCLComObject object has been told that it owns the component, then when that interface's reference count reaches zero, it will destroy its associated component. It's told it owns the component by calling its FreeOnRelease method.
All this is designed to make it easier to wrap VCL components into COM objects. If that's not your goal, then you'll probably be fighting against several other unexpected design aspects along the way, so you might wish to re-evaluate your motivation for making your components implement interfaces in the first place.
A component is supposed to be owned and destroyed by something else, typically a form. In that scenario, reference count is not used. If you pass a component as an interface reference it would be very unfortunate if it was destroyed when the method returns.
Therefore, reference counting in TComponent has been removed.