How can I set an external variable to nil inside a method?

280 views Asked by At

I'd like to subclass TThread in order to have a thread class that, when FreeOnTerminate = True, sets to nil its reference variable. In other words, I want do do something like this:

TFreeAndNilThread = class(TThread)
private
  FReferenceObj: TFreeAndNilThread;  //??  // <-- here I'm holding the reference to myself
protected
  procedure Execute;
  procedure DoTerminate; override;  // <-- here I'll set FReferenceObj to nil after inherited (only if FreeAndNil = True)
public
  constructor Create(CreateSuspended: Boolean; var ReferenceObj); reintroduce;  <-- untyped param like FreeAndNil and TApplication.CreateForm ??
end;

The consumer code should be like this:

var
  MyThread: TFreeAndNilThread;

begin
  MyThread := TFreeAndNilThread.Create(True, MyThread);
  MyThread.FreeOnTerminate := True;
  MyThread.Resume;
end;

...and then I could safely test for Assigned(MyThread).

I see how FreeAndNil manages to set a reference object passed by untyped param to nil, but it does this locally. In my case I should "save" it to a class field (FReferenceObj) and make it nil in another place (DoTerminate).

How can I correctly pass, store and retrieve it? I can think of passing and storing the address of MyThread instead of MyThread itself, but I hope there is a more elegant way.

Thank you!

1

There are 1 answers

6
David Heffernan On BEST ANSWER

You need to store a pointer to the variable.

type
  TFreeAndNilThread = class(TThread)
  private
    FReferenceObj: ^TFreeAndNilThread;
  end;

Then you need to pass in the reference in the constructor:

type
  TFreeAndNilThread = class(TThread)
  private
    FReferenceObj: ^TFreeAndNilThread;
  public
    constructor Create(var ReferenceObj: TFreeAndNilThread);
  end;

Then you can implement it like this:

constructor TFreeAndNilThread.Create(var ReferenceObj: TFreeAndNilThread);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FReferenceObj := @ReferenceObj;
end;

When the thread dies you set the reference to nil like this:

ReferenceObj^ := nil;

For convenience, since you'd likely treat this as a base class from which you derive other classes, you may prefer to make the parameter to the constructor untyped. You can just do that without making other changes to the code. When you do so the constructor looks like this:

constructor Create(var ReferenceObj);

This will only do you good if the code that sets the reference to nil is in the same thread as all other code that attempts to reference MyThread. Otherwise you will have a race condition.

I'm fairly sure that your idea is not the solution to your problem. However, the above does show you how to take a reference to a variable, which is what you asked for.