Is this code valid:
TMyObj = class(Tobject)
public
FThread: TThread;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TMyObj.Create(AOwner: TComponent);
begin
inherited;
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
FThread.start;
end;
destructor TMyObj.Destroy;
begin
inherited;
end;
my question is after i do
destructor TMyObj.Destroy;
begin
inherited;
end;
I mean after the object TMyObj is free, what will be the value of FThread inside
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
No, this code is not valid.
It is important to know that FThread is not captured, but the reference to the containing instance. So the anonymous method code rather looks like this:
FThread := TThread.createAnonymousThread(
procedure
begin
while not Self.FThread.CheckTerminated do sleep(10000);
end);
The compiler generates an object that implements the anonymous method with a field called Self that captures the reference to the TMyObj instance. If that instance gets destroyed while the anonymous method is alive within the thread, it will have a dangling reference.
A thread created with TThread.CreateAnonymousThread() is destroyed automatically when it finishes. So if you store that in a field for later access, you at least need to turn FreeOnTerminate off and not reference it via a captured variable to the thread itself, but rather with TThread.Current. Within the anonymous method, this will give you the thread that method is running in.
Related
I have a TMyFileStream that extend TFileStream.
type
TMyFileStream = class(TFileStream)
In TMyFileStream.Destroy i have some procedure used for logging server downloads. The file stream is passed in the response stream, when the client close connection with server or terminate download of the response the object is destroyed and the destructor log information like bytes send/file size.
This procedure that could raise exception first of call parent decostructor. I want protect the parent destructor from child exception.
basic example
destructor TMyFileStream.Destroy;
begin
MyExceptionMethod();
inherited;
end;
First solution i found is call the inherited destructor first of all
destructor TMyFileStream.Destroy;
begin
inherited;
MyExceptionMethod();
end;
another solution is catch and ignore exception
destructor TMyFileStream.Destroy;
begin
try
MyExceptionMethod();
except
// nothing
end;
inherited;
end;
another solution is catch and call the inherited destructor in finally
destructor TMyFileStream.Destroy;
begin
try
try
MyExceptionMethod();
except
// nothing
end;
finally
inherited;
end;
end;
what is the right way?
An alternative would be to override BeforeDestruction, there inherited is not related to the actual de-allocation of the instance's memory.
I have TMyClass, a class derived from TObject. It has a TTimer. Every few minutes, from Timer.OnTimer I check a web page. When the web page changes, I am done and I want to free MyClass. How do I free it?
My question is similar to this one BUT my 'control' is not a TControl. It is descendent of TObject. So, Messages won't work.
Obviously, the solution will be to derive my class from TControl or higher. But let's say I don't want to do that. What would be the solution in this case?
The basic idea behind using a message is correct: ensure that the object gets freed at a later point, after whatever code is currently calling it is finished.
A few years ago, I wrote a Delayed Action unit that gives you a simple way to accomplish this same effect without a TControl. You just call DelayExec and pass an anonymous method to it that will free the object, and it sets up a message internally that makes it happen once the message queue gets pumped.
To receive messages you need to have window handle. You can allocate one using AllocateHWnd, something like
type
TMyClass = class(TObject)
private
FHandle: HWND;
procedure MyWndProc(var Msg: TMessage);
public
constructor Create; virtual;
destructor Destroy; override;
end;
constructor TMyClass.Create();
begin
inherited Create();
FHandle := AllocateHWnd(myWndProc);
end;
destructor TMyClass.Destroy;
begin
DeallocateHWnd(FHandle);
inherited;
end;
procedure TMyClass.MyWndProc(var Msg: TMessage);
begin
case Msg.Msg of
CM_RELEASE: begin
Free;
end;
else Msg.Result := DefWindowProc(FHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
end;
Now you can post messages to the object using the FHandle as demonstrated in the post youre reffering to.
I wonder why, but I can not simply debug my simple program. Loaded method is ignored, it is never executed. No idea why. Look:
TGridObj = class (TComponent)
private
FPen1:TPen;
FBrush1:TBrush;
FChange:TNotifyEvent;
protected
procedure Loaded; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnChange:TNotifyEvent read FChange write FChange;
property Pen1:TPen read FPen1 write FPen1;
property Brush1:TBrush read FBrush1 write FBrush1;
end;
.
.
.
constructor TGridObj.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPen1:=TPen.Create;
FPen1.OnChange:=FChange;
FBrush1:=TBrush.Create;
FBrush1.OnChange:=FChange;
end;
destructor TGridObj.destroy;
begin
FPen1.Free;
FBrush1.Free;
inherited;
end;
procedure TGridObj.Loaded();
begin
inherited Loaded;
ShowMessage(''); // this is never executed;
FPen1.OnChange:=FChange;
FBrush1.OnChange:=FChange;
end;
.
.
procedure TForm1.FormCreate(Sender: TObject);
begin
Grid:=TGridObj.Create(nil);
Grid.OnChange:=ev1.OnChange;
Form1.InsertComponent(Grid);
end;
Thanx
Loaded is only called when the component's properties are streamed from the form file. Since you are creating it at runtime, Loaded does not get called. This is by design.
Your code needs some work anyway to allow for the OnChange event to be modified at runtime and have that change filter down to the pen and brush. I'd do it like this:
TGridObj = class (TComponent)
private
FPen1: TPen;
FBrush1: TBrush;
FChange: TNotifyEvent;
procedure DoChange(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnChange: TNotifyEvent read FChange write FChange;
property Pen1: TPen read FPen1;
property Brush1: TBrush read FBrush1;
end;
constructor TGridObj.Create(AOwner: TComponent);
begin
inherited;
FPen1 := TPen.Create;
FPen1.OnChange := DoChange;
FBrush1 := TBrush.Create;
FBrush1.OnChange := DoChange;
end;
destructor TGridObj.Destroy;
begin
FBrush1.Free;
FPen1.Free;
inherited;
end;
procedure TGridObj.DoChange(Sender: TObject);
begin
if Assigned(FChange) then
FChange(Sender);
end;
Now there's no need for Loaded or anything like that. Because you wait until the OnChange events of the pen and brush actually fire before accessing FChange.
By the way, in your code it's a mistake to add property setters for Pen1 and Brush1 that modify the underlying fields. That leads to leaks and all sorts of mess. Also, be warned that exposing the pen and brush as public properties allows clients of TGridObj to change the OnChange event. And that subverts TGridObj.OnChange.
OnLoaded will only be executed if the component is loaded from a form resource (dfm file).
If the component is created at run time in code only, it will not be executed.
Update:
I recommend to design components so that they can be created and configured at run time too - which means I avoid overriding OnLoaded. The advantage is that no package installation / component registration is needed.
I started learning Delphi two days ago but I got stuck. I broke down because nothing goes my way so I decided to write here. I wanted to create class that would have a field with its own TTimer object and which will perform some action at some time interval. Is it even possible? Suppose we have such code:
Sth = class
private
public
clock:TTimer;
procedure clockTimer(Sender: TObject);
constructor Create();
end;
constructor Sth.Create()
begin
clock.interval:=1000;
clock.OnTimer := clockTimer;
end;
procedure Sth.clockTimer(Sender: TObject);
begin
//some action on this Sth object at clock.interval time...
end;
My similar code copiles but it doesn't work properly. When I call the constructor the program crashes down (access violation at line: clock.interval:=1000;). I don't know what
Sender:TObject
does but I think that's not the problem. Is it possible to create such class I want to?
You have not created the timer. Declaring a variable is not enough. You do need to create the timer.
constructor Sth.Create()
begin
clock := TTimer.Create(nil);
clock.interval:=1000;
clock.OnTimer := clockTimer;
end;
And you should destroy it too. Add a destructor to the class
destructor Destroy; override;
and implement it like this
destructor Sth.Destroy;
begin
clock.Free;
inherited;
end;
I would also recommend that you make your clock field have private visibility. It's not good to expose the internals of a class like that.
TMyClass = class
private
FClock: TTimer;
procedure ClockTimer(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
end;
....
constructor TMyClass.Create
begin
inherited;
FTimer := TTimer.Create(nil);
FTimer.Interval := 1000;
FTimer.OnTimer := ClockTimer;
end;
destructor TMyClass.Destroy;
begin
FTimer.Free;
inherited;
end;
Note that I have included calls to the inherited constructor and destructor. These are not necessary in this class since it derives directly from TObject and the constructor and destructor for TObject is empty. But if you change the inheritance at some point, and make your class derive from a different class, then you will need to do this. So, in my view, it is good practise to include these calls always.
I discovered a strange problem in Delphi XE concerning a generic type def.
I created a derivative of TObjectList that include a number of classes (in example fIndex). Then I create a descendant class TMyButtonlist that contains a link to an owner.
The object fIndex is not created, so must be nil on destruction. But when I set the fOwer variable, the fIndex variable gets the same pointer, and crashes at destruction.
type
TMyObjectList<T:class> = class(TObjectList<T>)
private
fIndex:TStringList;
public
destructor Destroy; override;
end;
TMyButtonList = class(TMyObjectList<TButton>)
private
Owner:TObject;
end;
destructor TMyObjectList<T>.Destroy;
begin
fIndex.Free;
inherited;
end;
procedure TMainForm.btnClick(Sender: TObject);
var
Buttonlist:TMyButtonList;
begin
Buttonlist:=TMyButtonList.Create;
Buttonlist.Owner := Self;
ButtonList.Free;
end;