I'm passing created object to a constructor of another object which need an Interface which that object implements.
ISomeInterface = interface
['{840D46BA-B9FB-4273-BF56-AD0BE40AA3F9}']
end;
TSomeObject = class(TInterfacedObject, ISomeinterface)
end;
TSomeObject2 = class
private
FSomeInterface: ISomeinterface;
public
constructor Create(SomeObject: ISomeInterface);
end;
var
Form1: TForm1; // main form
SomeObject: TSomeObject;
constructor TSomeObject2.Create(SomeObject: ISomeInterface);
begin
FSomeInterface := SomeObject;
end;
// main form creating
procedure TForm1.FormCreate(Sender: TObject);
var SomeObject2: TSomeObject2;
begin
SomeObject := TSomeObject.Create;
// SomeObject2 := TSomeObject2.Create(nil); // ok
SomeObject2 := TSomeObject2.Create(SomeObject); // not ok
try
// do some things
finally
SomeObject2.Free;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
SomeObject.Free; // if passed to a SomeObject2 Constructor - freeing it causing av
end;
After I close main form it gives me an AV and a memory leak - whole main form has leaked.
If I'm passing nil to a TSomeObject constructor everything is well. Is compilator freeing FSomeInterface by reference counting and I'm shouldn't try to free SomeObject in mainForm destructor? How can I avoid it?
TSomeObject inherited from TInterfacedObject and thus is reference counted. Your instance of TSomeObject is not reference counted and should be removed or replaced by an interface variable.
If you need the instance of TSomeObject created in FormCreate, you should assign it to a variable of type ISomeInterface, so that the reference counting will work for that, too.
Another approach is to inherit from TInterfacedPersistant instead of TInterfacedObject to avoid the reference counting.
To explain what is happening in your code:
procedure TForm1.FormCreate(Sender: TObject);
var SomeObject2: TSomeObject2;
begin
{ Here you create the instance and assign it to a variable holding the instance.
After this line the reference count of the instance is 0 }
SomeObject := TSomeObject.Create;
// SomeObject2 := TSomeObject2.Create(nil); // ok
{ Using the instance as a parameter will increase the reference count to 1 }
SomeObject2 := TSomeObject2.Create(SomeObject); // not ok
try
// do some things
finally
{ Freeing SomeObject2 also destroys the interface reference FSomeInterface is
pointing to (which is SomeObject), decreasing the reference count to 0, which
in turn frees the instance of TSomeObject. }
SomeObject2.Free;
end;
{ Now, after SomeObject is freed, the variable points to invalid memory causing the
AV in FormDestroy. }
end;
Related
I have a COM class, which looks something like this:
TRadioTracer = class(TAutoObject, IRadioTracer)
Now, I can do
var
obj: TRadioTracer;
begin
obj := TRadioTracer.Create;
// some other code
obj.Free;
obj.CleanupInstance;
obj.FreeInstance;
end;
These are from System.pas
procedure TObject.FreeInstance;
begin
CleanupInstance;
_FreeMem(Pointer(Self));
end;
procedure TObject.CleanupInstance;
{$IFDEF PUREPASCAL}
var
ClassPtr: TClass;
InitTable: Pointer;
begin
{$IFDEF WEAKREF}
_CleanupInstance(Self);
{$ENDIF}
ClassPtr := ClassType;
repeat
InitTable := PPointer(PByte(ClassPtr) + vmtInitTable)^;
if InitTable <> nil then
_FinalizeRecord(Self, InitTable);
ClassPtr := ClassPtr.ClassParent;
until ClassPtr = nil;
TMonitor.Destroy(Self);
end;
{$ELSE !PUREPASCAL}
// some other code
procedure TObject.Free;
begin
// under ARC, this method isn't actually called since the compiler translates
// the call to be a mere nil assignment to the instance variable, which then calls _InstClear
{$IFNDEF AUTOREFCOUNT}
if Self <> nil then
Destroy;
{$ENDIF}
end;
Which one should I use to free the COM object?
Use the interface type to store a reference to the object. It will be destroyed as soon as there is no reference to it left:
var
obj: IRadioTracer;
begin
obj := TRadioTracer.Create;
obj.DoThings;
end; // obj will be freed here automatically
When you use the COM-Object in a different application or via a TAutoObjectFactory then you will only know the interface type. You have no access then to the concrete class type. That's an additional reason why to prefer the interface type here over the class type.
In case you are using the class type to reference the object you need to call Free to destroy it.
I am having issue with declaring a unique global variable for each connection of IdTCPServer. What i am trying to do here is.
TMyContext = class(TIdServerContext)
public
Tag: Integer;
Queue: TIdThreadSafeList;
FPacketBuffer: Pointer;
PacketBufferPtr: Integer;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
end;
and then accessing the variable using TMyContext(AContext).FPacketBuffer, but i get an access violation error when there is a connection active and a new connection tries to connect. here is what is in my idTcpConnect and idTcpDisconnect
procedure TMainFrm.MainSckConnect(AContext: TIdContext);
begin
TMyContext(AContext).Queue.Clear;
TMyContext(AContext).Tag := -1;
GetMem(TMyContext(AContext).FPacketBuffer,65536);
end;
procedure TMainFrm.MainSckDisconnect(AContext: TIdContext);
Var Client: TClientInfo;
begin
//If TMyContext(AContext).Queue.Count > 0 Then TMyContext(AContext).Queue.Clear;
TMyContext(AContext).Queue.Clear;
FreeMem(TMyContext(AContext).FPacketBuffer);
If AContext.Data <> nil Then Begin
Client := Pointer(AContext.Data);
Clients.Remove(Client);
Client.Free;
AContext.Data := nil;
End;
end;
The error occures when getmem is called in idtcpconnect, i think i am doing it all wrong, i am not sure how i can have a unique global variable for each context.
Make sure you are assigning your class type to the TIdTCPServer.ContextClass property before activating the server at runtime, eg:
procedure TMainFrm.FormCreate(Sender: TObject);
begin
MainSck.ContextClass := TMyContext;
end;
You can't change the class of a [already created] object instance to a different type. The object is of the class it was instantiated at creation time.
You can safely cast any object to it's own class or any class it inherits of, because the object IS of that class. In a hard cast (like you're doing), you're telling the compiler you know what you're doing, for example:
type
TMyButton: TButton
public
FMyField: array[1..50] of byte;
end;
var
Button: TButton;
begin
//next line is valid, a variable of type TButton can reference any object
//inheriting from TButton or a TButton instance directly
Button := TMyButton.Create(nil);
//next line contains a valid cast, because Button contains a reference to
//a instance of TMyButton
TMyButton(Button).FMyField[10] := 5;
//valid, a TButton variable referencing a TButton instance
Button := TButton.Create(nil);
//next line is invalid and may cause an AV or in the worst case
//you may corrupt memory by doing that
TMyButton(AButton).FMyField[20] := 5;
end;
The fact is, in your OnConnect event, you get an already created instance of TIdContext (or a descendant type).
If you want this object to belong to your class, you have to first ask the server to create objects of that class, via the ContextClass property. You have to do this before the Active property of the server is set to true.
procedure TMyForm.Init;
begin
MyServer.ContextClass := TMyContext;
MyServer.Active := True;
end;
And finally, if you have object references, you have to create the objects on the context constructor, or add a Late create mechanism if you don't want to waste memory and you don't use it too often:
TMyContext = class(TIdServerContext)
private
FQueue: TIdThreadSafeList;
public
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
property Queue: TIdThreadSafeList read FQueue;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
inherited;
FQueue := TIdThreadSafeList.Create(Parameters);
end;
destructor TMyContext.Destroy;
begin
FQueue.Free;
inherited;
end;
PHEWWWW! i was killing myself about what is wrong, i was thinking that the FPacetBuffer variable isnt unique to each connection but after alot of debugging and commenting out code sections i saw the problem and i was like WHATTT!!!!
In processing a login packet data i declared a PChar variable and copied data using StrLCopy to it and retrieved the size of the data and then assigned a null character myself to it (and that was the problem line).
Size := (Packet.BufferSize-SizeOf(TLoginPacket));
GetMem(UserName,Size);
StrLCopy(UserName, PChar(Cardinal(Packet)+SizeOf(TLoginPacket)),Size);
UserName[Size] := #0; <--- This Line here
The size variable was holding real size + 2 in it.
Thanks for all the help guys :)
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 ?
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;
When using Interfaces in Delphi and overriding reference counting, it is possible to bypass the_Release calls Delphi makes when an interface reaches a reference count of zero.
But - when mixing classes and interfaces (which is very useful) the _Release method is ALWAYS called no matter what. The problem is that in the sample code below, the local object is nill-ed, but _Release is still called - except on invalid memory. Depending on memory operations in the application, an exception can result when _Release is called on the nilled localObject's old location or no exception if the memory was not re-used.
So, can the compiler generated call to _Release be "removed/blocked/avoided/killed/redirected/vmt hijacked/terminated/smacked/etc etc etc"? If this can be achieved you have proper pure interfaces in Delphi.
unit TestInterfaces;
interface
uses
Classes,
SysUtils;
type
ITestInterface = interface
['{92D4D6E4-A67F-4DB4-96A9-9E1C40825F9C}']
procedure Run;
end;
TTestClass = class(TInterfacedObject, ITestInterface)
protected
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure Run;
end;
TRunTestClass = class(TObject)
protected
FlocalInterface : ITestInterface;
FlocalObject : TTestClass;
public
constructor Create;
destructor Destroy; override;
procedure Test;
end;
procedure RunTest;
procedure RunTestOnClass;
var
globalInterface : ITestInterface;
implementation
procedure RunTest;
var
localInterface : ITestInterface;
localObject : TTestClass;
begin
try
//create an object
localObject := TTestClass.Create;
//local scope
// causes _Release call when object is nilled
localInterface := localObject;
localInterface.Run;
//or global scope
// causes _Release call when exe shuts down - possibly on invalid memory location
globalInterface := localObject;
globalInterface.Run;
finally
//localInterface := nil; //--> forces _Release to be called
FreeAndNil( localObject );
end;
end;
procedure RunTestOnClass;
var
FRunTestClass : TRunTestClass;
begin
FRunTestClass := TRunTestClass.Create;
FRunTestClass.Test;
FRunTestClass.Free;
end;
{ TTheClass }
procedure TTestClass.Run;
begin
beep;
end;
function TTestClass._AddRef: Integer;
begin
result := -1;
end;
function TTestClass._Release: integer;
begin
result := -1;
end;
{ TRunTestClass }
constructor TRunTestClass.Create;
begin
FlocalObject := TTestClass.Create;
FlocalInterface := FlocalObject;
end;
destructor TRunTestClass.Destroy;
begin
//..
FlocalObject.Free;
//FlocalObject := nil;
inherited;
end;
procedure TRunTestClass.Test;
begin
FlocalInterface.Run;
end;
end.
There's no practical way to achieve what you are looking for. The compiler is going to emit the calls to _Release and in order to whack them you would need to find all the call sites. That's not practical.
I'm afraid the only viable approach when reference counted lifetime management is disabled is to ensure that you finalize (i.e. set to nil) all your interface references before calling Free.
When you use Interfaces you do not need to free your objects any more. interfaced objects will released automatically when there is no any references to same object.
In your sample you must delete _Release and _Addref functions in TTestClass they are defined in TInterfacedObject class.
In RunTest procedure you not need to Free the localObject only in finally section set globalInterface to nil. after end of procedure localInterface will destroy the local object automatically.
try
... use your code
...
finnaly
globalInnterface := nil;
end;
And about TTestRun.Destroy just left this destructor blank. you must not Free the FlocalObject.
TTestRun.Destroy;
begin
inherited;
end;
Here is my code example:
type
TMyBaseClass = class
public
procedure SomeProc; virtual;
end;
TMyChildClass = class(TMyBaseClass)
public
procedure SomeProc; override;
end;
var
SomeDelegate: procedure of object;
procedure TMyBaseClass.SomeProc;
begin
ShowMessage('Base proc');
end;
procedure TMyChildClass.SomeProc;
begin
ShowMessage('Child proc');
// here i want to get a pointer to TMyBaseClass.SomeProc (NOT IN THIS CLASS!):
SomeDelegate := SomeProc;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
with TMyChildClass.Create do
try
// there will be "Child proc" message:
SomeProc;
finally
Free;
end;
// there i want to get "Base proc" message, but i get "Child proc" again
// (but it is destroyed anyway, how coud it be?):
SomeDelegate;
end;
The one way I know is:
procedure TMyChildClass.BaseSomeProc;
begin
inherited SomeProc;
end;
procedure TMyChildClass.SomeProc;
begin
ShowMessage('Child proc');
SomeDelegate := BaseSomeProc;
end;
The 2nd is to change SomeProc declaration from override to reintroduce:
TMyChildClass = class(TMyBaseClass)
public
procedure SomeProc; reintroduce;
end;
and then cast self as TMyBaseClass (do not use as cast):
SomeDelegate := TMyBaseClass(self).SomeProc;
Also note that your code will give Access Violation because you call SomeDelegate on already freed object.
Adding a type declaration and some typecasting works but comes with some notes of warning.
As you've mentioned it yourself, the call to somedelegate after the instance has been freed doesn't AV. This is because your SomeProc method doesn't use any instance variables, all it does is calling ShowMessage.
Should you add any instance variables to the call, you even might still get away with it if the memory has not been reassigned. It would be an AV waiting to happen.
Bottom line:
don't call methods off destroyed objects.
setting a global variable from within an instance of a class that survives the lifetime of the class is not considered good design.
in a ideal design, there should be no need for a child class to revert a call anyhow to the ancestor's method, other than by calling inherited.
Code changes
...
type
TSomeDelegate = procedure of object;
var
SomeDelegate: TSomeDelegate;
...
procedure TMyChildClass.SomeProc;
var
method: TMethod;
begin
ShowMessage('Child proc');
// here i want to get a pointer to TMyBaseClass.SomeProc (NOT IN THIS CLASS!):
method.Code := #TMyBaseClass.SomeProc;
method.Data := Self;
SomeDelegate := TSomeDelegate(method);
end;